typescript - 类型 'Foo' 不能赋值给类型 'T extends string ? string | Foo : Foo'
我有以下代码:
class Foo<T> {
x?: T extends string ? string | Foo<T> : Foo<T>
}
function bar<T>(): Foo<T> {
const x: Foo<T> = { }
return { x }
}
为什么编译器会对 return { x } 报以下错误?
Type 'Foo<T>' is not assignable to type 'T extends string ? string | Foo<T> : Foo<T>'. ts(2322)
Foo<T> 应该同时满足 string | Foo<T> 和 Foo<T>,所以无论 T extends string 是否成立,它也应该满足 Foo.x 的类型,对吗?
答案#1
我同意,无论 T 是什么,类型 Foo<T> 都应该可以赋值给 T extends string ? string | Foo<T> : Foo<T>。这实际上有点难以验证,因为如果 T 是一个联合类型(例如 number | Date),那么 Foo<T> 将 不会 是一个联合类型(例如 Foo<number | Date>),但是 T extends string ? string | Foo<T> : Foo<T> 将 会 是一个联合类型(例如 Foo<number> | Foo<Date>)。 这是因为 T extends string ? ... : ... 是一个分布式条件类型,它会在求值前将联合类型拆分成各个成员,然后在之后重新组合成一个新的联合类型。因此,条件类型总是可以独立于 T 赋值给 Foo<T> 的事实,具体取决于 Foo<T> 的定义,以及它在 T 中是 协变 的(关于协变请参见这个问答讨论)。
但是编译器看不到这个。 为什么?
一般来说,依赖于泛型类型参数的条件类型的评估会被编译器延迟。在 microsoft/TypeScript#46429 中进行了一些工作,允许如果一个类型可赋值给该条件类型的真分支和假分支,则该类型可赋值给该条件类型,但这只适用于不使用infer关键字的非分布式类型。由于你有一个分布式类型,因此在这里不起作用。
因此,对 T extends string ? string | Foo<T> : Foo<T> 的评估会被推迟,直到 T 被指定为止。在 bar() 的函数体内部,T 未指定,因此条件类型在这里不会被评估。它对编译器来说本质上是不透明的,除非该值也是完全相同的条件类型,否则编译器无法验证你可以将一个值赋给它。而 Foo<T> 不是。因此编译器会报错。
代码的 Playground 链接