请考虑以下代码:
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;
游乐场链接。
类型Foo
和Y
完全相同,除了Foo
有一个通用参数Prop
,而Y
只使用一个名为Prop
的类型别名(类型别名不是必需的,Y
可能只是TestTuple extends Record<"test", string>[] ? true : false
但我想使它们的声明完全相同)。所以,Foo<"test">
(别名为类型X
)和Y
应该具有相同的类型,对吧?显然不是。X
为类型false
,而Y
为true
型。将TestTuple
中的other
属性更改为字符串或完全删除该属性会导致X
和Y
都为 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 }
,因为根据我的经验,它只对这些失败。希望有人能想出更好的答案。
但我可以提供一种解决方法。
解决方法
与其将TestTuple
与Record<...>[]
进行比较,不如将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;