對不起,標題很難,但請看一下以下最小表示:
// We define a couple of symbols
const A: unique symbol = Symbol();
const B: unique symbol = Symbol();
// And create an "enum" of them
const Fs = {
A,
B
} as const;
// This is the type A | B
type F = typeof Fs[keyof typeof Fs];
// This is our "input" types
type Foo = {
[Fs.A]: string,
[Fs.B]: number,
}
// This is our "output" types
type Bar = {
[Fs.A]: number,
[Fs.B]: string,
}
// We define a Mapping for the key (A or B) to be a function taking an
// input of type defined in Foo, and spitting out an output of type defined in Bar
type Mapping<T extends F> = (arg: Foo[T]) => Bar[T];
// We then define a map record as being a list of all such functions
type MapRecord = {
[key in F]: Mapping<key>
}
// Here is the action mapping. It does indeed comply with map record.
const mappings: MapRecord = {
[Fs.A]: (arg: string) => 123,
[Fs.B]: (arg: number) => "asdf",
} as const;
// And here we execute the mapping. Can you spot the error?
function doMapping<T extends F>(t: T, arg: Foo[T]): Bar[T] {
const mapper: Mapping<T> = mappings[t];
return mapper(arg);
}
看起來完全沒問題。但是在那一const mapper...行上,我們實際上得到了以下錯誤:
Type 'Mapping<unique symbol> | Mapping<unique symbol>' is not assignable to type 'Mapping<T>'.
Type 'Mapping<unique symbol>' is not assignable to type 'Mapping<T>'.
Types of parameters 'arg' and 'arg' are incompatible.
Type 'Foo[T]' is not assignable to type 'string'.
Type 'string | number' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.
mapping[t]當我們知道它必須只是一個時,它似乎認為結果可能是任何可能的記錄。
為什么?否則我如何說服打字稿?
uj5u.com熱心網友回復:
幾個邊注:
第一的:
const mappings: MapRecord = {
[Fs.A]: (arg: string) => 123,
[Fs.B]: (arg: number) => "asdf",
} as const;
不要將顯式型別MapRecord與as const. mappings將被推斷為MapRecord。as const完全不影響型別mappings。因此,您需要應用一個或另一個機器人,而不是兩者。
第二:
主要的問題是在這條線:mappings[t]。mappings[t]回傳函式的聯合。當你想呼叫一個函式的聯合時,它們的引數是相交的。因此你得到number & string === never. 請參閱檔案:
同樣,逆變位置中同一型別變數的多個候選會導致推斷出交叉型別:
請看相關回答,文章,文章
為了推斷映射并允許它,您還應該mappings作為引數傳遞。我通常curry它。
您可以使用這種方法:
// We define a couple of symbols
const A: unique symbol = Symbol();
const B: unique symbol = Symbol();
// And create an "enum" of them
const Fs = {
A,
B
} as const;
// This is our "input" types
type Foo = {
[Fs.A]: string,
[Fs.B]: number,
}
// This is our "output" types
type Bar = {
[Fs.A]: number,
[Fs.B]: string,
}
// Here is the action mapping. It does indeed comply with map record.
const mappings = {
[Fs.A]: (arg: string) => 123,
[Fs.B]: (arg: number) => "asdf",
} as const;
// This is the type A | B
type F = typeof Fs[keyof typeof Fs];
const doMapping = <
Key extends symbol,
Value extends (arg: any) => any, // MAIN DRAWBACK
Mapping extends Record<Key, Value>
>(mapping: Mapping) =>
<
Type extends keyof Mapping,
>(type: Type, arg: Parameters<Mapping[Type]>[0]): ReturnType<Mapping[Type]> =>
mapping[type](arg)
const result = doMapping(mappings)(Fs.A, 's') // number
const result2 = doMapping(mappings)(Fs.B, 42) // number
const error = doMapping(mappings)(Fs.B, 'a') // expected error
操場
Key- 是推斷的主引數的鍵
Value- 是推斷的函式
Mapping- 是推斷的引數。
您可能已經注意到它類似于我們如何解構 JS 物件。以similar某種方式可以推斷它們。演算法:推斷每個嵌套的鍵和值,然后將它們組裝成一個資料結構。
Type - 推斷出具有適當約束的第一個引數
您可以在我的博客中找到有關函式引數推斷的更多資訊
就個人而言,我認為最安全的方法是回傳一個函式:
// We define a couple of symbols
const A: unique symbol = Symbol();
const B: unique symbol = Symbol();
// And create an "enum" of them
const Fs = {
A,
B
} as const;
// This is our "input" types
type Foo = {
[Fs.A]: string,
[Fs.B]: number,
}
// This is our "output" types
type Bar = {
[Fs.A]: number,
[Fs.B]: string,
}
// Here is the action mapping. It does indeed comply with map record.
const mappings = {
[Fs.A]: (arg: string) => 123,
[Fs.B]: (arg: number) => "asdf",
} as const;
type Mappings = typeof mappings
const doMapping = <Type extends keyof Mappings,>(type: Type) =>
mappings[type]
const result = doMapping(Fs.A) // (arg:string)=>number
操場
簡單易行。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/326552.html
上一篇:如何在回傳Observable的函式中使用promise/async?
下一篇:反應形式等于密碼驗證
