TypeScript-自定义类型防护

时间:2020-10-07 07:01:17

标签: typescript typescript-typings

我有一个检查对象是否为“普通对象”的函数-也就是说,它不是任何类的实例,是像let x = { a: 1 };那样初始化的对象,我想为此函数添加类型保护,到assert的值与interface ([key: string]: any)匹配。

由于某种原因,该函数不会缩小类型。可能是因为我定义的“普通对象”和任何[key: string]:都不是同一件事(例如,一个类的实例也可以匹配[key: string]: any)。不过,我希望我的isPlainObject()函数断言,如果成功,则该值匹配[key: string]: any)。

这是我在操场上尝试过的。我还提供了一个“手册”示例来检查特定属性-可以使用。

Playground

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

1 个答案:

答案 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>
相关问题