我可以很容易地使用一个常量字符串值来缩小联合类型:
type Payload1 = { /* ... arbitrary type ... */ };
type Payload2 = { /* ... arbitrary type ... */ };
type T1 = { type: 'type1', payload: Payload1 }
type T2 = { type: 'type2', payload: Payload2 }
type T = T1 | T2;
const fn = (value: T) => {
if (value.type === 'type1') {
value; // Typescript knows `value is T1`
}
if (value.type === 'type2') {
value; // Typescript knows `value is T2`
}
};
这里只有两种情况:
value.type
是常数"type1"
value.type
是常数"type2"
但是如果我扩展T
,允许payload
是单个项或数组,会怎么样呢?现在有四种可能性:value.type
是"type1"
,value.payload
* 不是 *array
value.type
是"type1"
,value.payload
* 是array
value.type
是"type2"
,value.payload
* 不是 *array
value.type
是"type2"
,value.payload
* 是array
下面是一个示例:
type Payload1 = {};
type Payload2 = {};
type T1Single = { type: 'type1', payload: Payload1 }
type T1Batch = { type: 'type1', payload: Payload1[] };
type T2Single = { type: 'type2', payload: Payload2 }
type T2Batch = { type: 'type2', payload: Payload2[] };
// Here's T, now with 4 types instead of 2:
type T = T1Single | T1Batch | T2Single | T2Batch;
const fn = (value: T) => {
if (value.type === 'type1' && !Array.isArray(value.payload)) {
value; // Typescript says `value is T1Single | T1Batch`?!
// How does `T1Batch` remain in the union if `value.payload` isn't an array??
}
if (value.type === 'type1' && Array.isArray(value.payload)) {
value; // Typescript says `value is T1Single | T1Batch`?!
// How does `T1Single` remain in the union if `value.payload` is an array??
}
if (value.type === 'type2' && !Array.isArray(value.payload)) {
value; // Typescript says `value is T2Single | T2Batch`?!
// How does `T2Batch` remain in the union if `value.payload` isn't an array??
}
if (value.type === 'type2' && Array.isArray(value.payload)) {
value; // Typescript says `value is T2Single | T2Batch`?!
// How does `T2Single` remain in the union if `value.payload` is an array??
}
};
操场
为什么typescript只是部分缩小了类型,我如何才能实现4种情况下的完全缩小值?
编辑:看起来if
中的多个条件是无关紧要的; typescript仅基于Array.isArray
努力缩小:
type Payload = {};
type Single = { payload: Payload }
type Batch = { payload: Payload[] };
const fn = (value: Single | Batch) => {
if (!Array.isArray(value.payload)) {
value; // Typescript says `value is Single | Batch`?!
}
if (Array.isArray(value.payload)) {
value; // Typescript says `value is Single | Batch`?!
}
};
2条答案
按热度按时间4uqofj5v1#
您试图将
T
视为判别式并集,但payload
属性未被识别为判别式。要将属性视为有效的判别式,它必须包含单位/文字类型。您的type
属性有效,因为"type1"
和"type2"
是字符串文字类型。但是数组和Payload
类型是对象类型,而不是文字类型。所以你不能检查value.payload
并让它缩小value
本身的明显类型。请注意,
Array.isArray(value.payload)
确实充当value.payload
属性的类型保护,但由于该属性不是判别式,因此这种收缩不会传播到value
本身。在microsoft/TypeScript#42384上有一个开放的特性请求,允许属性类型保护传播到包含对象。不过,它还不是语言的一部分,之前对它的请求被拒绝了,因为它被认为对嵌套属性的每个类型保护检查合成新类型太昂贵了。现在,如果你想获得这样的行为,你可以编写一个自定义的类型保护函数,根据
payload
属性是否是数组来缩小值。就像这样:然后调用
hasArrayPayload(value)
,而不是编写Array.isArray(value.payload)
内联:Playground链接到代码
bvjveswy2#
不可能一次缩小多个步骤的类型。
相反,您可以使用类型 predicate 或类型保护
例如:
下面是上面例子的codesandbox链接:playground