【发布时间】:2021-01-22 02:28:42
【问题描述】:
我有一个函数可以检查一个对象是否是一个“普通对象”——也就是说,它不是任何类的实例,一个像 let x = { a: 1 }; 这样初始化的对象我想给这个函数添加一个类型保护,到 @ 987654325@ 表示检查值与interface ([key: string]: any) 匹配。
由于某种原因,该函数没有缩小类型。也许是因为我定义的“普通对象”和[key: string]: any 根本不是一回事(例如,一个类的实例也可以匹配[key: string]: any)。不过,我希望我的 isPlainObject() 函数断言,如果成功,则值匹配 [key: string]: any)。
这是我在操场上尝试过的。我还提供了一个“手册”示例来检查特定属性 - 这很有效。
interface IndexedObject {
[key: string]: any
}
interface FooObject {
foo: "bar"
}
// This works - an example from the handbook
const isFooObject = (value: any): value is FooObject => (
typeof value === 'object'
&& value !== null
&& typeof (value as FooObject).foo !== 'undefined'
);
/**
* Check if the value is a "plain" JavaScript object initialized
* with { } (not instance of any class)
*
* In addition to the runtime check, this function should assert that the
* argument matches IndexedObject interface
*
*/
const isPlainObject = (value: any): value is IndexedObject => (
typeof value === 'object'
&& value !== null
&& value.constructor === Object
&& Object.getPrototypeOf(value) === Object.prototype
);
function test<T>(value: T): T {
if (isFooObject(value)) {
const x = value; // T & FooObject - it works
}
if (isPlainObject(value)) {
const x = value; // T - doesn't work
}
return value;
}
更新1:
我想我不会在这里使用类型保护,因为 IndexedObject 太宽泛,会产生意想不到的结果。这将告诉 TypeScript,否则该值永远不会与 IndexedObject 匹配——这是不可取的。我将改用内联断言。
除非可以定义一个匹配“普通对象”的类型?
尽管如此,我仍然很好奇为什么类型保护不起作用。另外,如果我在 if 块中返回值,则在 if 块之后,类型将永远不会 - playground 2
【问题讨论】:
-
我认为我不应该在这里使用类型保护,而是使用内联类型断言。 IndexedObject 接口对于这个函数来说太宽泛了,可能会导致意想不到的事情 - 例如。如果它确实有效,TypeScript 会假设在 if { } 之后类型不能匹配 IndexedObject - 这在这里不会是真的。
-
尽管如此,我仍然对为什么这不起作用感到困惑,我刚刚测试了一个更前卫的情况 - 如果我在 if { } 块中返回值,在块之后, typeof 值永远不会。新的游乐场案例:shorturl.at/abqEH
-
这可能是一个错误。似乎它只是忽略了类型,因为属性的类型为
any,这可能不会注册为“有意义的”。例如,如果您将属性类型更改为number,它将注册。顺便说一句,可以使用帮助器Record(此处为Record<string, any>)编写此类类型。
标签: typescript typescript-typings