高阶函数参数类型的类型推断错误

幻影

假设我想编写一个函数,该函数接受某个T类型的对象和另一个值,该P类型应该以某种方式受T限制,例如P应该是T的键数组。

我可以轻松地编写它:

function bar<T, P extends keyof T>(obj: T, p: P[]) {
  // use p to index obj somehow
  return obj;
}

bar({ a: 1, b: 'foo' }, ['a']); // Ok
bar({ a: 1, b: 'foo' }, ['a', 'b']); // Ok
bar({ a: 1, b: 'foo' }, ['a', 'b', 'c']); // Error: 'c' is not valid key

想象一下,然后我想将该函数用作高阶方法的参数,该函数应与第二个参数一起接受它,arg并仅使用thisand调用它arg

class Indexed {
  constructor(public a: number = 1) {}
  public app<P>(f: (obj: this, arg: P) => this, arg: P) {
    return f(this, arg);
  }
}

const a = new Indexed().app(bar, ['a']); // Error, `Type 'string' is not assignable to type '"a" | "app"'.`
const a = new Indexed().app(bar, ['wtf']); // The same

如果bar直接使用,一切都会按预期进行:

bar(new Indexed(), ['a']); // Ok
bar(new Indexed(), ['wtf']); // Err, as expected

操场

问题是:如何编写app使其接受/拒绝参数的方式相同bar

请注意,一般而言,我不了解bar先验的限制,因此我无法P以与中相同的界限进行限制bar

贾卡尔兹

我认为这只是TypeScript扩展["foo","bar"]到的一种情况,string[]因为它没有意识到您需要类型保留字符串文字["foo", "bar"](或至少一个字符串文字数组Array<"foo"|"bar">)。在您的bar()函数中,P被约束为keyof任何内容都提示编译器不要将字符串文字扩展为字符串,但是Pin中没有这样的提示Indexed.app()

您需要想出一种方法来修改Indexed.app()签名,以便P在可能的情况下以狭窄的方式推断出提示,而实际上并不对其进行限制(因为您不知道P会是什么,正如您所说),或者您需要来提示/指定P调用 时应缩小的方法Indexed.app()


修改的签名app()以执行此操作目前需要一些怪异的技巧,并且直到且除非此更改,它看起来像这样:

type Narrowable =
  | string
  | number
  | boolean
  | symbol
  | object
  | undefined
  | void
  | null
  | {};

class Indexed {
  constructor(public a: number = 1) {}
  public app<
    N extends Narrowable,
    P extends N | [] | { [k: string]: N | P | [] }
  >(f: (obj: this, arg: P) => this, arg: P) {
    return f(this, arg);
  }
}

const a = new Indexed().app(bar, ["a"]); // okay
const b = new Indexed().app(bar, ["wtf"]); // error "wtf" not assignable to "a"|"app"

如果呼叫者记得这样做,则在呼叫站点上进行提示就不太麻烦了:

class Indexed {
  constructor(public a: number = 1) {}
  public app<P>(f: (obj: this, arg: P) => this, arg: P) {
    return f(this, arg);
  }
}
const a = new Indexed().app(bar, ["a" as "a"]); // okay
const b = new Indexed().app(bar, ["wtf" as "wtf"]); // error "wtf" not assignable to "a"|"app"

或者您可以忘记提示,而自己手动指定type参数:

const c = new Indexed().app<["a"]>(bar, ["a"]); // okay
const d = new Indexed().app<["wtf"]>(bar, ["wtf"]); // error "wtf" not assignable to "a"|"app"

好吧,希望这些帮助之一。祝好运!

链接到代码

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章