【问题标题】:TypeScript - custom type guardTypeScript - 自定义类型保护
【发布时间】: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)。

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

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;

}

更新1:

我想我不会在这里使用类型保护,因为 IndexedObject 太宽泛,会产生意想不到的结果。这将告诉 TypeScript,否则该值永远不会与 IndexedObject 匹配——这是不可取的。我将改用内联断言。

除非可以定义一个匹配“普通对象”的类型?

尽管如此,我仍然很好奇为什么类型保护不起作用。另外,如果我在 if 块中返回值,则在 if 块之后,类型将永远不会 - playground 2

【问题讨论】:

  • 我认为我不应该在这里使用类型保护,而是使用内联类型断言。 IndexedObject 接口对于这个函数来说太宽泛了,可能会导致意想不到的事情 - 例如。如果它确实有效,TypeScript 会假设在 if { } 之后类型不能匹配 IndexedObject - 这在这里不会是真的。
  • 尽管如此,我仍然对为什么这不起作用感到困惑,我刚刚测试了一个更前卫的情况 - 如果我在 if { } 块中返回值,在块之后, typeof 值永远不会。新的游乐场案例:shorturl.at/abqEH
  • 这可能是一个错误。似乎它只是忽略了类型,因为属性的类型为any,这可能不会注册为“有意义的”。例如,如果您将属性类型更改为number,它将注册。顺便说一句,可以使用帮助器Record(此处为Record&lt;string, any&gt;)编写此类类型。

标签: typescript typescript-typings


【解决方案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>

【讨论】:

  • 感谢您的回答。你是对的,它似乎只是在泛型内部表现得很奇怪,所以它可能是一个错误。 shorturl.at/nqOU3 你可能知道,我想澄清一下你上面的例子没有错。它必须是 T &“类型”,而不仅仅是“类型”——否则,它会破坏泛型。只是为了澄清而指出这一点,因为从您的回答中听起来这也是一个错误。
猜你喜欢
  • 2020-11-11
  • 2015-12-19
  • 2018-04-13
  • 1970-01-01
  • 1970-01-01
  • 2020-02-01
  • 2019-01-04
  • 1970-01-01
  • 2020-11-23
相关资源
最近更新 更多