为什么 TypeScript 有时只会将不可能的交集视为 'never'?
Why does TypeScript only sometimes treat an impossible intersection as 'never'?
TypeScript 有时会决定两种类型(如果相交)没有任何兼容的值。这个空交集称为 never
,这意味着您不能提供满足两种类型的值:
type Bread = {
shape: "loafy"
};
type Car = {
shape: "carish"
};
// Contradiction1: Immediately resolved to 'never'
type Contradiction1 = Bread & Car;
但是,这似乎工作不一致。如果冲突的属性不在类型的顶层,TypeScript 会错过它并且不会按我预期的方式运行:
// Wrap the contradicting types
type Garage = { contents: Car };
type Breadbox = { contents: Bread };
// Contradiction2: Garage & Breadbox
// Expected: Should immediately reduce to never
type Contradiction2 = Garage & Breadbox;
这是一个错误吗?为什么 TypeScript 会这样?
这是预期的行为,因为 TypeScript 中的 属性 路径可以 任意深度 ,同时沿途更改类型。例如,这样写是完全合法的:
declare class Boxed<T> {
contents: T;
doubleBoxed: Boxed<this>
};
declare const b: Boxed<string>
// m: Boxed<Boxed<Boxed<Boxed<Boxed<string>>>>>
const m = b.doubleBoxed.doubleBoxed.doubleBoxed.doubleBoxed;
因此,对于任意类型,"can" 存在实际上无限数量的属性,其中任何一个都可能具有您的程序中从未见过的新类型。
这对 never
很重要,因为您可能会这样写:
// I am here to cause trouble.
type M<T, S> = T extends { nested: { nested: { nested: any } } } ?
S :
{ el: T, nested: M<{ nested: T }, S> };
type F1 = {
prop: M<F1, "foo">
};
type F2 = {
prop: M<F2, "bar">
};
declare const f1: F1;
// f1.prop.nested.nested.nested: "foo"
f1.prop.nested.nested.nested;
declare const f12: F1 & F2;
// OK, infinitely
f12.prop.el.prop.el.prop.el.prop.el.prop;
// 'never' at depth 4...
f12.prop.nested.nested.nested;
确实无法预测 中的 ,您需要寻找可能导致 never
的表达式 - M
的定义没有给我们任何提示;你必须作为一个人真正理解这段代码,才能知道去哪里探索以找到嵌套的 never
.
事实上,如果您可以针对任意深度的 属性 访问解决此 "correctly",您可以通过构造执行算术的类型(这已经是可能的)。显然这是不可能的,因此 TypeScript 不会尝试超越生成类型的顶级属性的容易解决的情况。
TypeScript 有时会决定两种类型(如果相交)没有任何兼容的值。这个空交集称为 never
,这意味着您不能提供满足两种类型的值:
type Bread = {
shape: "loafy"
};
type Car = {
shape: "carish"
};
// Contradiction1: Immediately resolved to 'never'
type Contradiction1 = Bread & Car;
但是,这似乎工作不一致。如果冲突的属性不在类型的顶层,TypeScript 会错过它并且不会按我预期的方式运行:
// Wrap the contradicting types
type Garage = { contents: Car };
type Breadbox = { contents: Bread };
// Contradiction2: Garage & Breadbox
// Expected: Should immediately reduce to never
type Contradiction2 = Garage & Breadbox;
这是一个错误吗?为什么 TypeScript 会这样?
这是预期的行为,因为 TypeScript 中的 属性 路径可以 任意深度 ,同时沿途更改类型。例如,这样写是完全合法的:
declare class Boxed<T> {
contents: T;
doubleBoxed: Boxed<this>
};
declare const b: Boxed<string>
// m: Boxed<Boxed<Boxed<Boxed<Boxed<string>>>>>
const m = b.doubleBoxed.doubleBoxed.doubleBoxed.doubleBoxed;
因此,对于任意类型,"can" 存在实际上无限数量的属性,其中任何一个都可能具有您的程序中从未见过的新类型。
这对 never
很重要,因为您可能会这样写:
// I am here to cause trouble.
type M<T, S> = T extends { nested: { nested: { nested: any } } } ?
S :
{ el: T, nested: M<{ nested: T }, S> };
type F1 = {
prop: M<F1, "foo">
};
type F2 = {
prop: M<F2, "bar">
};
declare const f1: F1;
// f1.prop.nested.nested.nested: "foo"
f1.prop.nested.nested.nested;
declare const f12: F1 & F2;
// OK, infinitely
f12.prop.el.prop.el.prop.el.prop.el.prop;
// 'never' at depth 4...
f12.prop.nested.nested.nested;
确实无法预测 中的 ,您需要寻找可能导致 never
的表达式 - M
的定义没有给我们任何提示;你必须作为一个人真正理解这段代码,才能知道去哪里探索以找到嵌套的 never
.
事实上,如果您可以针对任意深度的 属性 访问解决此 "correctly",您可以通过构造执行算术的类型(这已经是可能的)。显然这是不可能的,因此 TypeScript 不会尝试超越生成类型的顶级属性的容易解决的情况。