Typescript - 可选类型谓词

3

我希望构建一个类型判断函数,当数字是实数时返回true,并同时使类型更严格:

function isRealNumber(input: number | undefined | null): input is number {
    return input !== undefined && input !== null && Number.isFinite(input);
}

然而,在某些情况下,当否定时,这会产生不正确的类型,例如:

const myNumber: number | null = NaN as any;
if (isRealNumber(myNumber)) {
    const b = myNumber;  // b is number, correct
} else {
    const b = myNumber;  // b is `null`, should be `null | number`
}

这可以通过在条件语句中使用多个语句来解决,但这并不是最理想的方法。
function isRealNumber(input: number | undefined | null): boolean {
    return input !== undefined && input !== null && Number.isFinite(input);
}

const myNumber: number | null = NaN as any;
if (isRealNumber(myNumber) && myNumber !== null && myNumber !== undefined) {
    const b = myNumber;  // b is number, correct
} else {
    const b = myNumber;  // b is `null | number`, correct
}

有没有一种方法可以在 TypeScript 中编写一个函数,它能正确地缩小类型,而不会在否定时有时产生错误的类型?
1个回答

2
您正在寻找“单向”或“细粒度”类型谓词,如microsoft/TypeScript#15048中所请求的,但这在TypeScript中目前不被直接支持。该问题仍未解决,但不清楚是否会在此处发生任何事情。
然而,在该问题中提到了一个解决方法mentioned。如果您将受保护的类型从input is number更改为input is RealNumber,其中RealNumber是一些可分配但严格比number更窄的类型,那么事情就会开始工作。
虽然在类型系统中,RealNumbernumber 之间没有实际区别,但是您可以通过使用一种称为品牌原语的技术来“伪造”一个区别。这是通过将一个原始类型(如 number)与一个包含幻影“品牌”或“标记”属性的对象类型进行 交集 来实现的。例如:
function isRealNumber(input: number | undefined | null): input is number & { __realNumber?: true } {
    return input !== undefined && input !== null && Number.isFinite(input);
}

const myNumber: number | null = NaN as any;
if (isRealNumber(myNumber)) {
    const b = myNumber;  // b is number & {__realNumber?: true}
} else {
    const b = myNumber;  // b is number | null
}

看起来更合理。如果isRealNumber(myNumber)true,那么b会被缩小到number & {__realNumber?:true}。这个{__realNumber?:true}属性在运行时实际上不会存在(它是一个“幻影”属性),但这并不重要,因为b仍然是一个number。当isRealNumber(myNumber)false时,b根本不会被缩小。

游乐场代码链接


太棒了,这帮了大忙,非常感谢! - Daniel Centore

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接