具有泛型的类型别名表现出与非泛型类型不同的行为



请考虑以下代码:

type TestTuple = [
{ test: "foo" },
{
test: "bar";
other: 1;
}
];
type Foo<Prop extends string> = TestTuple extends Record<Prop, string>[]
? true
: false;
type X = Foo<"test">;
type Prop = "test";
type Y = TestTuple extends Record<Prop, string>[]
? true
: false;
// X is type false
const x: X = false;
// Y is type true
const y: Y = true;

游乐场链接。

类型FooY完全相同,除了Foo有一个通用参数Prop,而Y只使用一个名为Prop的类型别名(类型别名不是必需的,Y可能只是TestTuple extends Record<"test", string>[] ? true : false但我想使它们的声明完全相同)。所以,Foo<"test">(别名为类型X)和Y应该具有相同的类型,对吧?显然不是。X为类型false,而Ytrue型。将TestTuple中的other属性更改为字符串或完全删除该属性会导致XY都为 true,这是预期行为。

所以,我的问题是:这是为什么?这是编译器中的错误吗?如果是这样,是否已提交我无法找到的问题?或者,这是在打字稿中处理泛型的某种奇怪方式?

更新:TypeScript 4.2 已修复此问题:指向代码的游乐场链接


我已经提交了关于这个的microsoft/TypeScript#41613,在减少到以下最小的例子之后:

type What<K extends string> =
{ x: { y: 0, z: 1 } } extends { x: { [P in K]: 0 } } ? true : false;
type Huh = What<"y">; // expect true but got false!

TypeScript 的首席架构师 Anders Hejlsberg 评论道:

在确定是否延迟条件类型的解析时,我们将检查和扩展类型的"最允许的实例化"相关联。扩展类型最宽松实例化的约束最终是{ x: { [index: string]: 0 } },但实际上它应该是{ x: { } }的。这是一个简单的修复,我会将其包含在此 PR 中。

所以希望它最终会在新的 PR 中修复,并可能通过 TypeScript 4.2 合并到 TypeScript 中。(更新:它已被合并。 如果是这样,我希望这应该解决您问题中的问题,而不是用{x: ...}包装索引类型,而是用元组类型包装它。

在此之前,您应该考虑使用类似于@Temoncher答案中的解决方法。

操场链接到代码

不幸的是,我个人不知道。它与字典类型有关,例如Record{ [K in Prop]: any },因为根据我的经验,它只对这些失败。希望有人能想出更好的答案。

但我可以提供一种解决方法。

解决方法

与其将TestTupleRecord<...>[]进行比较,不如将TestTuple[number]Record<...>进行比较

type Foo<P extends string> = TestTuple[number] extends Record<P, string>
? true
: false
// true
type X = Foo<'test'>
// also true
type Y = TestTuple extends Record<'test', string>[]
? true
: false;

最新更新