我有一个检查对象是否为“普通对象”的函数-也就是说,它不是任何类的实例,是像let x = { a: 1 };
那样初始化的对象,我想为此函数添加类型保护,到assert
的值与interface
([key: string]: any)
匹配。
由于某种原因,该函数不会缩小类型。可能是因为我定义的“普通对象”和任何[key: string]:
都不是同一件事(例如,一个类的实例也可以匹配[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;
}
Update1:
我认为我不会在这里使用类型保护,因为IndexedObject的范围太广并且会产生意外的结果。这将告诉TypeScript,否则该值将永远不匹配IndexedObject-这是不希望的。我将改用内联断言。
除非可以定义与“普通对象”匹配的类型?
尽管如此,我仍然很好奇为什么类型保护器不起作用。另外,如果我在if块内部返回值,则在if块之后,该类型将永远不会-playground 2
答案 0 :(得分:1)
并不是一个真正的答案,但是泛型的参与似乎使它吞噬了类型。如我的评论所述,这似乎是一个错误。
function testTypeGuard<T>(value: unknown): value is T
{
return true; // Lies
}
function genericFunction<T>(value: T)
{
if (testTypeGuard<Record<string, any>>(value))
console.log(value); // T
if (testTypeGuard<Record<number, any>>(value))
console.log(value); // T
if (testTypeGuard<Record<any, string>>(value))
console.log(value); // T & Record<any, string>
if (testTypeGuard<Record<any, number>>(value))
console.log(value); // T & Record<any, number>
if (testTypeGuard<Record<string, string>>(value))
console.log(value); // T & Record<string, string>
if (testTypeGuard<Record<number, string>>(value))
console.log(value); // T & Record<number, string>
}
// No generics:
const value: unknown = {};
if (testTypeGuard<Record<string, any>>(value))
console.log(value); // Record<string, any>
if (testTypeGuard<Record<number, any>>(value))
console.log(value); // Record<number, any>
if (testTypeGuard<Record<any, string>>(value))
console.log(value); // Record<any, string>
if (testTypeGuard<Record<any, number>>(value))
console.log(value); // Record<any, number>
if (testTypeGuard<Record<string, string>>(value))
console.log(value); // Record<string, string>
if (testTypeGuard<Record<number, string>>(value))
console.log(value); // Record<number, string>