TypeScrip不理解记录类型必须返回正确类型 [英] Typescript does not understand that record types must return the correct type
本文介绍了TypeScrip不理解记录类型必须返回正确类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
很抱歉标题很难,但请看以下最低限度的表示:
// 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]
的结果可能是任何可能的记录,而我们知道它必须只是一个。
为什么?另外,我如何才能让打字稿信服呢?
推荐答案
附注:
首先:
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
。请参阅docs:
同样,同一类型变量在逆变量位置的多个候选项会导致推断交集类型:
为了推断映射并允许它,您还应该将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
-使用适当的约束推断第一个参数
您可以在我的blog
中找到有关函数参数推理的详细信息我个人认为最安全的方法是返回一个函数:
// 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
轻松简单。
这篇关于TypeScrip不理解记录类型必须返回正确类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文