首页 文章

具有union和object类型的Inconsitent TypeScript行为

提问于
浏览
4

我们有以下代码段:

interface A {
  isEmpty(x: any[] | string | object): boolean;
  isEmptyObject(x: object): boolean;
}

const a: A = <A>{};

a.isEmptyObject({});
a.isEmptyObject({a: 1});

a.isEmpty([]);
a.isEmpty('');
a.isEmpty({});
a.isEmpty({a: 1});

当我尝试编译它时,它会崩溃:

a.ts(14,12): error TS2345: Argument of type '{ a: number; }' is not assignable to parameter of type 'string | object | any[]'.
  Object literal may only specify known properties, and 'a' does not exist in type 'string | object | any[]'.

如果联合类型( string | object | any[] )包含它可以工作的类型 object (使用 {a: 1}isEmptyObject 调用正常),这怎么可能?

主要问题:这是一个TypeScript错误,或者应该如何编写输入可能是任何值或字符串或对象的数组(但不是任何原语,我不想 anyObject )?

使用TypeScript 2.3.4进行测试 .

3 回答

  • 4

    这不是一个错误,而是一个支持使用参数对象的API的功能 . 以jQuery-s ajax函数为例 . 它将一个对象作为参数 .

    interface JQueryAjaxParam {
        url: string;
        data?: any;
    }
    
    ...
    
    $.ajax({
        url: "",
        datta: "" // notice the typo here
    });
    

    You do want a compilation error in this case ,即使 {url:"",datta:""} 实现了param接口 . 为了解决这个问题,他们添加了excess property checks for object literals as direct parameters . This is a good thing ,在类型检查器中提供更多信心,并且只有在人们尝试像您一样进行快速测试时才会导致错误 . 您必须牢记这一规则,这是额外安全的成本 .

    UPDATE: TypeScript 2.4另外介绍Weak types

    那么有这个签名的函数:

    function isEmptyObject(x : object) : void;
    

    在这里,意图很明确,这个功能旨在接受任何键的任何对象 . 仅接受空对象的函数几乎没有用处 . 所以他们添加了一个例外:当参数的类型为 object 时,则省略前一个检查 .

    那么该怎么办?没有 . 在实际使用中,你的功能会很好地检测 . 你可以尝试一下:

    interface A {
      isEmpty(x: any[] | string | object): boolean;
      isEmptyObject(x: object): boolean;
    }
    
    const a: A = <A>{};
    const param = {a: 1};
    
    a.isEmptyObject({});
    a.isEmptyObject(param);
    
    a.isEmpty([]);
    a.isEmpty('');
    a.isEmpty({});
    a.isEmpty(param);
    

    UPDATE

    您的示例是一个实际的用例,您希望它甚至可以使用文字 . 在Nitzan之后,我也建议使用函数重载 .

    interface A {
        isEmpty(x: any[]): boolean;
        isEmpty(x: string): boolean;
        isEmpty(x: object): boolean;
        isEmptyObject(x: object): boolean;
    }
    
    const a: A = <A>{};
    a.isEmptyObject({b: 6});
    a.isEmptyObject({a: 1});
    
    a.isEmpty([]);
    a.isEmpty('');
    a.isEmpty({});
    a.isEmpty({a: 1});
    
  • 1

    您可以通过添加其他签名轻松解决此问题:

    interface A {
        isEmpty(x: any[]): boolean;
        isEmpty(x: string | object): boolean;
        isEmptyObject(x: object): boolean;
    }
    
    a.isEmpty({ a: 1 }); // fine now
    

    code in playground


    编辑

    我不认为这是一个错误,如果有任何错误,这是错误消息,这是不是很有帮助 .

    我认为发生的事情是你可以在任何原始类型的联合中使用 object 而没有问题(在你的情况下),但是一旦你引入了非原始类型,那么你将得到错误,例如:

    isEmpty(x: string | object | Map<string, string>): boolean;
    

    会导致同样的错误 .
    object (这是非原始的)和另一个非原始类型的并集可能导致一种无法匹配的类型,并且编译器会混淆 .

    欢迎您在此案例中打开一个新问题,如果您这样做,请在此处分享链接,我想看看他们说了什么 .

  • 1

    Typescript在以下条件下编译失败:

    Object literal may only specify known properties, and 'a' does not exist in type 'string | object | any[]'.

    基本上 { a: 1 } 不能转换为 object 类型 .

    你可以这样做:

    interface A {
        isEmpty(x: any[] | string | { [index: string]: any }): boolean;
        isEmptyObject(x: { [index: string]: any }): boolean;
    }
    

    编辑:接下来看看docs它可能解释为:

    联盟类型在这里可能有点棘手,但它需要一点直觉才能习惯 . 如果值的类型为A | B,我们只知道它有A和B都有的成员 . 在这个例子中,Bird有一个名为fly的成员 . 我们无法确定变量是否为Bird |鱼有飞法 . 如果变量在运行时确实是Fish,则调用pet.fly()将失败 .

相关问题