ock*_*888 4 javascript typescript ecmascript-6
我正在为接口编写类型保护,我注意到我无法object使用inorObject.hasOwnProperty.call或缩小范围arg.hasOwnProperty。像这样:
interface Test {
quest: string;
}
function isTest(arg: unknown): arg is Test {
// this successfully narrows arg to "object"
if (typeof(arg) !== "object" || arg === null) {
return false;
}
if (!Object.hasOwnProperty.call(arg, "quest")) {
return false;
}
// at this point, I still cannot access arg.quest, because
// "Property 'quest' does not exist on type 'object'". The same thing happens
// if I invert the check and try to access it inside the conditional.
if (!arg.hasOwnProperty("quest")) {
return false;
}
// same problem, cannot access arg.quest because arg is still just "object"
if (!("quest" in arg)) {
return false;
}
// still the same problem.
return true;
}
Run Code Online (Sandbox Code Playgroud)
因此,我必须进行大量类型转换,这很烦人,因为我想要检查的实际接口有很多属性,并非所有属性都是基本原语。所以基本上,对于每个属性,我都必须制作如下所示的东西:
function isTest(arg: unknown): arg is Test {
if (typeof(arg) !== "object" || arg === null) {
return false;
}
if (!Object.hasOwnProperty.call(arg, "quest")) {
return false;
}
if (typeof((arg as {quest: unknown}).quest) !== "string") {
return false;
}
return true;
}
Run Code Online (Sandbox Code Playgroud)
...总的来说,我觉得如果我必须使用as我就做错了什么。那么,如何在object不为接口的每个属性编写单独的类型保护的情况下缩小接口范围呢?
至于为什么我需要如此强大的保护,这是因为这是在服务器中进行的,我正在检查发送到其 API 的有效负载,JSON.parse这样我就可以用它们进行复杂的操作,而不必担心中途发现数据是结构不良。
尽管通常不安全,但我发现它作为用户定义类型保护函数any的参数类型很有用:
function isTest(arg: any): arg is Test {
return (typeof arg === "object") && (arg !== null) &&
("quest" in arg) && (typeof arg.quest === "string");
}
Run Code Online (Sandbox Code Playgroud)
以上编译没有错误。不,它实际上并没有在实现中强制执行安全性,因为any有效地关闭了类型检查。但是,只要您验证实现是否在做正确的事情,usingany就会让编译器摆脱您的束缚,以便的调用者isTest()可以获得类型安全。
unknown不过,这本质上与 using和type assertions相同,您说过这会让您感觉自己做错了什么。
但请记住,无论如何,用户定义的类型保护函数的实现都是以这种方式工作的。编译器为您验证的唯一事情是您的返回类型是boolean. 当你说这样的结果可以被视为由类型谓词表示的类型保护时,它只是相信你。例如,你可以这样写:booleanarg is Test
function isBadTest(arg: unknown): arg is Test {
return Math.random() < 0.5; // no error
}
Run Code Online (Sandbox Code Playgroud)
因此,虽然您希望从编译器获得尽可能多的类型安全是有道理的,但您应该记住,实现用户定义的类型保护会将部分负担转移给您。在混合中添加一个as或一个any并不会真正放弃太多。
话虽这么说,让我忽略您正在编写自己的类型保护函数这一事实,而只注意您具有类型值unknown并且您试图让编译器查看它是否可以缩小到的问题Test,或者至少是具有quest您可以访问的属性的东西。
这里的问题是 TypeScript 没有内置类型保护来向对象类型的已知类型添加属性。正如您所指出的,您当前无法使用 来in执行此操作(请参阅microsoft/TypeScript#21732),并且您不能使用hasOwnProperty()来执行此操作(请参阅microsoft/TypeScript#18282)。如果你想要这样的类型保护,你必须自己编写。例如:
function hasProp<T extends object, K extends PropertyKey>(
obj: T,
key: K
): obj is T & Record<K, any> {
return key in obj;
}
function isTest2(arg: unknown): arg is Test {
if (typeof arg !== "object") return false;
if (arg === null) return false;
if (!hasProp(arg, "quest")) return false; // use it here,
if (typeof arg.quest !== "string") return false; // no error here
return true;
}
Run Code Online (Sandbox Code Playgroud)
不过,一旦您开始走这条路线,并记住您的最终目的是在运行时验证反序列化对象以查看它们是否符合 TypeScript 接口,我最终得出的结论是,您将需要类似这样的答案。在那里,我编写了一个迷你模式验证器,它可以从更简单的类型防护中构建复杂的类型防护。使用该代码,您的isTest()只是:
const isTest3: G.Guard<Test> = G.gObject({ quest: G.gString });
Run Code Online (Sandbox Code Playgroud)
其中G.gObject()接受属性类型保护的对象并为具有这些属性的对象生成类型保护,并且G.gString是验证其参数是否为 a 的类型保护string。
因此,您可能想查看其他答案,以避免必须为每个接口重新实现类型保护。
| 归档时间: |
|
| 查看次数: |
1276 次 |
| 最近记录: |