打字稿推断关键字推断错误的类型?

Typescript infer keyword inferring wrong type?

像这样的简单构造:

type Test<Source extends string[]> = 
    Source extends [infer Head, ...infer Tail] ? `${Head}${Head}` : never;

Typescript 抱怨 ${Head}${Head} 中的字符串插值说:Type `'Head'` is not assignable to type 'string | number | bigint | boolean'. Type 'Head' is not assignable to type 'number'.ts(2322)

...这对我来说意义不大,但我的搜索并没有取得成果。源必须是 string[] 所以 Head,如果它存在,一定是一个字符串吧?所以 ${Head} 必须完全有效。为什么 TS 抱怨无法将其分配给 number? String 也可以分配给 "string | number | bigint | boolean" 所以这个错误对我来说没有多大意义。我可以通过将字符串插值放在 "Head extends string ? ... : ... 子句之间来修复它,但我想知道为什么它首先是必要的。

是也不是。 看看下一个例子:

type Test0<Source extends string[]> =
    Source extends [infer Head, ...infer Tail] ? Head : never;

type O = Test0<[never, 'a']> // no error, because that is how NEVER works

因为string|never returns string.

我认为在上述情况下检查 Head 是否为字符串更安全。

错误的原因是 TypeScript 在推断类型之前不会考虑 extends string[] 约束。如果你看这个类型

type T<Head> = `${Head}`

您会看到相同的相当迟钝的错误消息(模板文字的推断有点靠不住)。由于 T 不会在这里推断出 Head,您当然可以只声明 type T<Head extends string> = ..,但另一种选择是在额外的条件中对 Head extends string 约束进行编码:

type T<Head> = Head extends string ? `${Head}` : never

这就是为什么 Test 类型检查你是否添加了额外的、看似多余的、有条件的:

type Test<Source extends string[]> = 
    Source extends [infer Head, ...infer Tail] 
    ? Head extends string ? `${Head}${Head}` : never 
    : never;

TypeScript playground