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

Apl*_*123 6 generics metaprogramming typescript

考虑以下代码:

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;
Run Code Online (Sandbox Code Playgroud)

游乐场链接

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

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

jca*_*alz 1

更新:TypeScript 4.2 已修复此问题:Playground 代码链接


在减少到以下最小示例后,我已就此提交了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!
Run Code Online (Sandbox Code Playgroud)

TypeScript 首席架构师Anders Hejlsberg评论

在确定是否推迟条件类型的解析时,我们将检查类型和扩展类型的“最许可实例化”联系起来。最允许的扩展类型实例化的约束最终是{ x: { [index: string]: 0 } },但实际上应该是{ x: { } }。这是一个简单的修复,我将把它包含在这个 PR 中。

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

在那之前,您应该考虑使用一种解决方法,例如@Temoncher 的回答中的解决方法。

Playground 代码链接