Bug报告
🔎 搜索词
提取可分配的TS2322
🕗 版本与回归信息
这是我在每个版本中尝试的行为,我查阅了关于泛型、可分配性和提取的常见问题解答。
测试了4.2.3、4.3.5、4.4.4和4.5.0-dev.20211018版本。
在4.1.5版本中,使用类型别名的示例也失败了。从4.2.x开始,4.2版本引入了更智能的类型别名保留,这可能解释了为什么那个示例从4.2.x开始就通过了。
⏯ Playground链接
带有相关代码的Playground链接
💻 代码
我已经尽可能地最小化了这个示例。使用 Extract
来定义字段类型似乎很重要。
type A<T> = {
val: Extract<number | string, T>;
};
type A_number = A<number>;
function f1(x: A_number): A<number| string> {
// Passes type checking.
return x;
}
function f2(x: A<number>): A<number | string> {
// Fails type checking with error TS2322:
// Type 'A<number>' is not assignable to type 'A<string | number>'.
// Type 'string | number' is not assignable to type 'number'.
// Type 'string' is not assignable to type 'number'.
return x;
}
🙁 实际行为
类型检查器报告错误TS2322:A<number>
不能分配给类型 A<string | number>
,因为 string | number
不能分配给 number
。它似乎是在错误的方向上检查分配性,因为分配性检查应该是 number
是否可以分配给 string | number
。
使用 A<number>
的类型别名不会产生相同的错误。我也无法在不使用 Extract
定义字段类型的情况下重现此错误。
🙂 预期行为
类型 A<number>
应该可以分配给类型 A<string | number>
,因为它是一个简单的对象类型,而 number
可以分配给 string | number
。
5条答案
按热度按时间xmd2e60i1#
A<T>
在T
中是逆变的,因为它是从另一种类型中减去T
而不是直接使用它。因此,对于T
的更宽类型将产生一个较窄的A<T>
,所以这个错误是可以预料到的。idfiyjo82#
A<T>
是对T
的逆变,因为它是从另一种类型中减去T
而不是将其原封不动地使用。因此,对于T
的更宽类型将产生一个较窄的A<T>
,所以这个错误是可以预料到的。我没有看到它是如何从另一种类型中减去
T
的,因此是逆变的。Extract<T, U>
被定义为“从 T 中提取那些可以分配给 U 的类型”。如果T
中的一个类型S
可以分配给U
,那么根据定义,S
也可以分配给U | V
。在A<T>
的情况下,我不认为扩大T
会缩小A<T>
的类型。执行类型替换应该使这一点变得清晰:最坏的情况下,
A<T | V>
等于A<T>
(例如,数字和字符串都不能分配给V
)。aoyhnmkz3#
对不起,我的错误,我把
Extract
和Exclude
混淆了。话虽如此,问题/错误似乎是 TS 以某种原因测量A<T>
作为逆变量:string | number
的确不能分配给number
,但这与A
的方向相反。所以出于某种原因,它似乎认为T
是逆变量的。🤔jc3wubiy4#
它认为
A
是 不变的 ;这也失败了:这对于一般条件类型是有意义的(考虑
type X<T> = {} extends T ? 1 : 2
;X<{}>
不能分配给或从X<{ x: string }>
);Extract
只是特殊情况,其中它只具有协变性。这里的真正错误是 TS 在这里进行协变性检查,而不是进行结构比较;协变性度量应该用
VarianceFlags.Unreliable
标记(这意味着协变性检查可能会产生假阴性,但不会产生假阳性)。wxclj1h55#
可能相关的: #43887