TS2339:在交替类型上无法识别属性


type NotifyOptions = {
className: string
timeout?: null | number
} & ({ text: string } | { html: string })

export default function notify(options: NotifyOptions) {
if(options.text) {
console.log(options.text)
}
}
类型"NotifyOptions"上不存在属性"text"。类型"{className:string;timeout?:number | null | undefined;}"上不存在属性"text";{html:string;}'。(2339(

ts游乐场

为什么说options.text不存在?我认为应该键入string|undefined

Typescript只允许您访问联合中每个成员的公共属性。

另一方面,它将不允许您访问仅在联合的一个成员中定义的属性。

工会一个成员身上的成员不存在于其他成员身上,这种假设在实践中可能是不正确的:他们可能存在,财产的类型可能不同。因此,假设options.text是字符串或未定义被认为是不安全的。

考虑以下示例:

const o = {
className: 'c1',
timeout: 3000,
html: 'ht',
text: 4
};
const opt: NotifyOptions = o;

我创建了一个NotifyOptions,其中文本是一个数字。

Typescript 3.2引入了非单元类型作为并集判别符

TypeScript 3.2通过放宽其认为是判别性质的规则,使缩小范围变得更容易。并集的公共属性现在被认为是判别式,只要它们包含某种单例类型(例如字符串文字、null或未定义(,并且不包含泛型。

你可以写:


type NotifyOptions = {
className: string
timeout?: null | number
} & ({ text: string; html?: undefined; } 
| { html: string; text?: undefined})

Typescript允许您访问属性,并将它们视为并集的判别符。

除此之外,不再允许将o常量分配给NotifyOptions。

游乐场链接

这个操场展示了一个允许"歧视工会"的工作示例。它的工作方式是,它使用Typescript实际可以利用的那种检查(文字相等(来分离特定的类型。参见歧视工会。

type NotifyOptions = {
className: string;
timeout?: null | number;
} & ({
kind: "html";
html: string;
}|{
kind: "text";
text: string;
});
export default function notify(options: NotifyOptions) {
if (options.kind === "text") {
console.log(options.text);
}
else if(options.kind === "html") {
console.log(options.html);
}
}

不幸的是,typescript的工作方式是,您只能引用在并集的所有情况下都存在的属性。因此需要具有kind属性。你可以看到jcalz在https://stackoverflow.com/a/65495776/2257198

如果您的案例真的可以不注意类型系统(例如,您只对属性的存在感兴趣,并且无论其类型如何都可以取消引用它(,那么对原始代码的最小更改就是使用in运算符,这样就消除了类似这样的编译器错误。

export default function notify(options: NotifyOptions) {
if("text" in options) {
console.log(options.text)
}
}

如果你不喜欢你的案例中的Typescript类型,你可以更进一步地使用javascript类型。。。

export default function notify(options: NotifyOptions) {
if("text" in options && typeof options.text === "string") {
console.log(options.text)
}
}

最新更新