簡體   English   中英

高階 function 參數類型的錯誤類型推斷

[英]Wrong type inference of higher-order function argument type

假設我想編寫 function ,它接受某種類型 T 的 object 和另一個值,其中類型 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

想象一下,然后我想使用 function 作為高階方法的參數,它應該與第二個參數arg一起接受它,然后用thisarg調用它:

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以使其接受/拒絕 arguments,就像bar一樣?

請注意,一般來說,我不知道bar apriori 的限制,所以我不能用與bar相同的界限來限制P

我認為這只是 TypeScript 將["foo","bar"]擴展為string[]的情況,因為它沒有意識到您需要類型來保持字符串文字的元組["foo", "bar"] (或至少一個字符串文字Array<"foo"|"bar"> )。 在你的bar() function 中, P被限制為keyof任何東西都暗示編譯器不要將字符串文字擴展到字符串,但Indexed.app()中的P不存在這樣的提示。

要么您需要想出一種方法來修改Indexed.app()簽名,以便暗示P應該在可能的情況下以狹窄的方式推斷而不實際限制它(因為您不知道P將是什么,正如您所說),或者您需要想出一種方法來提示/指定在調用Indexed.app()P應該是窄的。


修改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"

或者您可能會忘記提示並手動指定類型參數:

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

好的,希望其中之一有所幫助。 祝你好運!

鏈接到代碼

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM