我有以下代码:
type Inferred<T> = T extends (...args: (infer UnionType)[]) => any ? UnionType : never
function f(first: 'first', second: 'second', bool: boolean) {}
type T = Inferred<typeof f> // never
我希望推断的类型是联合类型'first' | 'second' | boolean
。我知道我可以这样得到它:
type Inferred<T> = T extends (...args: infer A) => any ? A : never
function f(first: 'first', second: 'second', bool: boolean) {}
type T = Inferred<typeof f>[number] // union type
或者等效地:
type Inferred<T> = T extends (...args: infer A) => any ? A[number] : never
function f(first: 'first', second: 'second', bool: boolean) {}
type T = Inferred<typeof f> // union type
为什么第一种方法不起作用?
发布于 2020-12-11 22:24:58
为了回答这个问题,让我们后退一步,从更广泛的角度来看待它。
TypeScript使用相同的语法(即[]
)用于元组和数组。但它们是明显不同的概念。为了简洁起见,我将在下面的段落中对元组使用()
(而不是标准TypeScript的[]
)。另外,请记住TypeScript中的T[] === Array<T>
。
按照惯例,元组是固定长度和异构的(可能包含不同类型的值)。(1, "Adam", 42)
是(number, string, number)
类型的元组。
数组通常是同构的(保存相同类型的值),并且具有任意长度。
在TypeScript中,由于联合类型的支持,两者之间的区别略显模糊。当使用T = number | string
时,[1, "Adam", 42]
也是一个完美的Array<T>
。这在没有联合类型支持的语言中是不可能的(并且会导致找到通常类似于any
的number | string
的上下界)。
在提供的示例中使用此知识
type Inferred<T> = T extends (...args: (infer UnionType)[]) => any ? UnionType : never
我们可以把它看作是“从Array<T>
类型的带参数的函数列表中获取T
”。
我们知道:function f(first: 'first', second: 'second', bool: boolean)
的参数类型列表是('first', 'second', boolean)
在这一点上,需要实现以下统一(:=:
):
('first', 'second', boolean) :=: Array<T>
这样,只要使用('first', 'second', boolean)
类型的值,就可以使用Array<T>
类型的值。实际上不存在这样的T,因此推断出never
。
在这里,看起来T = string | boolean
甚至T = 'first' | 'second' | boolean
是一个解决方案,但是:
type Arr = Array<'first' | 'second' | boolean>
type Tuple = ['first', 'second', boolean]
const a: Arr = ['first', 'second', true]
const t: Tuple = ['first', 'second', true]
let a1: Arr = t;
let t1: Tuple = a; // Type error no. 2322
Target requires 3 element(s) but source may have fewer.(2322)
或者在类型级别上:
type TEA = Tuple extends Arr ? true : false // true
type AET = Arr extends Tuple ? true : false // false
这也解释了为什么type Y = Inferred<(a: number, b: number, c: number) => void>
会产生number
总而言之,示例中的(infer UnionType)[]
似乎不支持推断元组类型,而只支持数组。
免责声明-我不熟悉TypeScript编译器的内部原理,这是我基于常识和直觉来解释这里发生的事情的最好尝试。
发布于 2020-12-11 21:50:42
这是因为有限参数的函数不能扩展(潜在的)无限参数的函数:
// X is false
type X =
((a: string, b: number) => any) extends ((...args: (string | number)[]) => any)
? true
: false;
后两个方法都使用元组类型(['first', 'second', boolean]
),它有一个已知的有限长度,这允许它们工作。
https://stackoverflow.com/questions/65258444
复制