Typescript 如果判别属性为union,则键入缩小错误
我用TypeScript编写了如下代码。 类型Typescript 如果判别属性为union,则键入缩小错误,typescript,Typescript,我用TypeScript编写了如下代码。 类型ABCD类似于状态对象,其kind属性表示状态的类型。因为它们中的一些(kind是'a'或'b')有一个共同的附加属性extra,所以我将它们的共同类型写为AB。其他的没有额外的属性,所以我将它们合并为CD,如下所示 type AB = { kind: 'a' | 'b', extra: string }; type CD = { kind: 'c' | 'd' }; type ABCD = AB | CD; function pr
ABCD
类似于状态对象,其kind
属性表示状态的类型。因为它们中的一些(kind
是'a'
或'b'
)有一个共同的附加属性extra
,所以我将它们的共同类型写为AB
。其他的没有额外的属性,所以我将它们合并为CD
,如下所示
type AB = {
kind: 'a' | 'b',
extra: string
};
type CD = {
kind: 'c' | 'd'
};
type ABCD = AB | CD;
function printExtra(x: ABCD) {
if (x.kind === 'c' || x.kind === 'd') return;
console.log(x.extra);
}
如果种类既不是'c'
也不是'd'
,则函数printExtra
将打印extra
属性。
我确信编译会通过,因为if语句断言x
不是CD
,而且AB
是该类型的唯一候选,显然x
是AB
。但是TypeScript编译器未能编译,并显示以下消息
类型“ABCD”上不存在属性“extra”。
类型“CD”上不存在属性“extra”
为什么上面的代码无法编译?我还尝试了以下代码,并确认它编译成功,但如果可能的话,我不想使用它,因为它在我的项目中有些冗余
type A = { kind: 'a', extra: string };
type B = { kind: 'b', extra: string };
type C = { kind: 'c' };
type D = { kind: 'd' };
type ABCD = A | B | C | D;
让你的逻辑更进一步:
type A = { kind: 'a', extra: string };
type B = { kind: 'b', extra: string };
type C = { kind: 'c' };
type D = { kind: 'd' };
type AB = A | B;
type CD = C | D;
type ABCD = AB | CD;
那么这也会起作用:
type AB = ({ kind: 'a' } | { kind: 'b' }) & { extra: string };
type CD = { kind: 'c' } | { kind: 'd' };
type ABCD = AB | CD;
让我们把这句话说得不那么冗长:
type Kind<T extends string> = { [K in T]: { kind: K } }[T];
type AB = Kind<'a' | 'b'> & { extra: string };
type CD = Kind<'c' | 'd'>;
type ABCD = AB | CD;
type-Kind={[K in T]:{Kind:K}}[T];
AB型=种类&{extra:string};
CD型=种类;
ABCD型=AB | CD;
您可以通过重构代码来检查其他方法
function printExtra(x: ABCD) {
if (x.kind === 'a' || x.kind === 'b') {
console.log(x.extra); // no error
}
}
有其他方法可以做到这一点。你可以用
AB型={
种类:“a”|“b”,
额外:字符串
};
类型CD={
种类:“c”|“d”
};
ABCD型=AB | CD;
函数断言B(x:ABCD):断言x是AB{
如果(x.kind!='a'&&x.kind!='b'){
抛出新错误(“不是字符串!”);
}
}
函数printExtra(x:ABCD){
sab(x);
const result=x;//AB
console.log(x.extra);//确定
}
缺点:您应该处理错误
就我个人而言,我更喜欢第二种方法,使用typeguards。这样更安全:
AB型={
种类:“a”|“b”,
额外:字符串
};
类型CD={
种类:“c”|“d”
};
ABCD型=AB | CD;
常数isAB=(x:ABCD):x是AB=>x.kind=='a'| | x.kind=='b'
函数printExtra(x:ABCD){
如果(伊莎伯(x)){
const result=x.extra;//AB
}
}
通过定义函数isCd(x:ABCD):x是CD{return x.kind=='c'| | | x.kind=='d'},您总是可以给编译器一些帮助
。至于为什么会发生这种情况,我不知道。类型ABCD
可以是更高的AB
或CD
。当类型ABCD=AB
时,您可以使用x.extra
,因为AB
具有extra
但当类型ABCD=CD
时,您不能调用x.extra
,因为CD
不包括extra
。