Jen*_*lly 3 typescript typeguards
假设我有以下用户定义的类型保护函数,用于检查值是否为上面的数字1000:
function isBigNumber(something: unknown): something is number {
return typeof something === "number" && something > 1000;
}
Run Code Online (Sandbox Code Playgroud)
然后我这样使用它:
const strOrNum: string | number = "asdf";
if (isBigNumber(someVar)) {
console.log(someVar * 10); // works because of type-guard
} else {
// here type of strOrNum is "string" and no longer "string | number"
}
Run Code Online (Sandbox Code Playgroud)
strOrNum我的问题是Else 块中的类型。
看起来类型防护检查类型,并且 TS 还使用类型防护来缩小 Else 块的类型范围,在本例中这不是我想要的。类型防护是否仅用于检查类型,而没有传递给它们的值的附加信息?
isBigNumber是否有解决方案,无需更改to的返回类型并且不必再次检查 If 语句的boolean类型?strOrNum
没有>1000类型。如果你正在处理一组有限的、已知的数字,你可以做类似的事情something is 1001 | 1002 | 1003 | 1004 | 1005,Typescript 会跟踪它(并且会理解在某种false情况下something仍然可能是(其他一些)number),但是你\xe2\x80 \x99不是。
这里的解决方案是 \xe2\x80\x9ctype 品牌,\xe2\x80\x9d 这实际上是在 Typescript\xe2\x80\x99s 结构类型系统中 \xe2\x80\x9cfake\xe2\x80\x9d 名义键入的一种方法。您可以安装多种方法和多个库,但为此我\xe2\x80\x99m 将使用我自己的,我只是将其称为As. 我\xe2\x80\x99ve包含As在这个答案的底部,并附有一些解释,但它\xe2\x80\x99s也可以将其用作黑匣子。
工作方式As是,你可以说某件事是,比如说,,As<"big-number">Typescript 会尊重这一点并跟踪这个大数字。它纯粹存在于类型系统中,并且从编译后的 Javascript 中完全消失。有了它,您可以编写只接受大数字的函数,可以编写确认大数字的类型保护器,等等。
关于这些品牌真正重要的事情之一是,如果你有一个 typeguard 说something is X & As<"whatever">,Typescript 会理解它something可能仍然是X,因为它可能是缺少的As<"whatever">部分something。这解决了你的打字保护问题。
所以,对于你的例子:
\ntype BigNumber = number & As<"big-number">;\n\nfunction isBigNumber(something: unknown): something is BigNumber {\n return typeof something === "number" && something > 1000;\n}\n\nfunction onBigNumber(value: BigNumber): void {\n console.log(value - 1000); // works because BigNumber extends number\n}\n\ndeclare const strOrNum: string | number;\n\nif (isBigNumber(strOrNum)) {\n console.log(strOrNum * 10); // works because of type-guard\n onBigNumber(strOrNum); // works because of type-guard\n} else {\n // here type of strOrNum is "string | number"\n if (typeof strOrNum === "number") {\n console.log(strOrNum * 100); // works because of type-guard\n onBigNumber(strOrNum); // ERROR, because strOrNum is number but not As<"big-number">\n }\n}\nRun Code Online (Sandbox Code Playgroud)\nAsdeclare abstract class As<Tag extends keyof never> {\n private static readonly $as$: unique symbol;\n private [As.$as$]: Record<Tag, true>;\n}\nRun Code Online (Sandbox Code Playgroud)\n如果您想理解这一点而不是仅仅将其视为黑匣子,请注意以下几点:
\ndeclare意味着TS不会\xe2\x80\x99t生成这个类的代码\xe2\x80\x94它告诉TS这个类的运行时JS已经存在,我们\xe2\x80\x99只是通知TS它的存在。那\xe2\x80\x99是一个谎言,在这种情况下\xe2\x80\x94这个类根本不存在\xe2\x80\x99。abstract阻止我们尝试new As,这会由于上面讨论的 \xe2\x80\x9clie\xe2\x80\x9d 而导致运行时错误。它不会阻止我们尝试编译class X extends As,并且还会导致运行时错误。不要\xe2\x80\x99 这样做。(declare class X extends As不过,如果你想扩大谎言,你可以。)Tag extends keyof never是As通用的,所以我们可以将它与许多不同的品牌一起使用。keyof never是确定与合法 JS 对象键 ( ) 对应的类型联合的奇怪最佳实践string | number | symbol。private [As.$as$]告诉 Typescript 该类的对象有一个私有成员,也就是说,它的结构是不同的且特定的。这意味着 Typescript\xe2\x80\x99s 结构类型系统会将其视为不同的类型。它\xe2\x80\x99s private,所以没有人可以访问它(这很好,因为这是另一个谎言),并且使用As.$as$,它被定义为 a unique symbol,保证我们不会\xe2\x80\x99t 与我们的任何名称发生冲突。应用这个品牌。Record<Tag, true>作为私有成员 \xe2\x80\x9cstores\xe2\x80\x9d 的类型,无论Tag传递给类\xe2\x80\x99s 泛型参数。使其成为Record允许多个品牌在同一对象上共存的方式。| 归档时间: |
|
| 查看次数: |
1074 次 |
| 最近记录: |