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

时间:2017-06-06 12:02:52

标签: typescript

我们有以下代码段:

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类型时(isEmptyObject{a: 1}的呼叫是否合适),这怎么可能?

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

使用TypeScript 2.3.4进行测试。

3 个答案:

答案 0 :(得分:4)

这不是一个错误,而是一个支持API-s的功能,它利用了参数对象。以jQuery-s ajax函数为例。它需要一个对象作为参数。

interface JQueryAjaxParam {
    url: string;
    data?: any;
}

...

$.ajax({
    url: "",
    datta: "" // notice the typo here
});

在这种情况下确实需要编译错误,即使{url:"",datta:""}实现了param接口。为了解决这个问题,他们添加了excess property checks for object literals as direct parameters这是一件好事,在类型检查器中提供更多信心,并且只有在人们尝试进行像您这样的快速测试时才会导致错误。你必须牢记这一规则,这是额外安全的代价。

更新: 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);

<强>更新

您的示例是一个实际的用例,您希望它甚至可以使用文字。在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 :(得分:1)

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

Person

基本上Sex无法转换为Object literal may only specify known properties, and 'a' does not exist in type 'string | object | any[]'.类型。

你可以这样做:

{ a: 1 }

编辑:在看了docs之后,可能会解释为:

  

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

答案 2 :(得分: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(这是任何不是原始的)和另一个非原始类型的联合可能导致一种无法匹配的类型,并且编译器会感到困惑。

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