maplike对象

时间:2016-07-26 12:04:07

标签: typescript

拥有一个可以包含任意数据的接口(例如通过库定义)并不罕见,如下所示:

interface Something {
  name: string;
  data: { [key: string]: any };
}

我遇到的问题是当我尝试访问或设置具有任意值的对象的值时,Typescript会出错。

let a: Something = {
  name: 'foo',
  data: { bar: 123 }
};

// Below errors with: 'Property "bar" does not exist on type {[key:string]: any}'
console.log(a.data.bar);
a.data.bar = 234;

这是Typescript中的错误还是有一种简单的方法可以避免这样的错误?

This Typescript playground清楚地说明了这个问题。

修改

我正在寻找一个选项,希望不会让我必须从a.data.bar重写整个代码库到a.data['bar']

2 个答案:

答案 0 :(得分:0)

如果您将类型定义为可索引,那么您需要访问如下属性:

a["data"].bar

您的游乐场示例:modified to work properly

更多关于Indexable Types

让我们从问题开始:

interface Something {
    name: string;
    data: { [key: string]: any };
}

Something界面中,有name类型的特定属性名string,因此编译器知道这意味着什么:

let a: Something = ...
console.log(a.name);

但是,如果我console.log(a["unknownKey"]),那么它怎么知道这个unknownKey是什么?它是对象中的有效属性吗?它是什么类型的?
编译器无法知道,因为您没有指定此对象具有此键,这就是您被迫使用索引表示法的原因。

如何解决这个问题?
好吧,您可以定义您知道的属性以及您已经在代码中使用的属性,例如,如果您使用属性nameidaddress,那么请在你的界面:

interface Something {
    id: string;
    name: string;
    address: string;
    data: { [key: string]: any };
}

其他属性可以作为索引保留。

您也可以使用课程:

interface Something {
    name: string;
    data: { [key: string]: any };
}

class MySomething {
    public name: string;
    public else: string;

    constructor(obj: Something) {
        this.name = obj.name;
        this.else = obj["else"];
    }
}

另一个选择当然是只使用绕过编译器类型检查的any

let a: any = ...
console.log(a.myProperty);

这显然不是一个好的解决方案,但是如果你只是在将旧代码迁移到打字稿时会遇到摩擦,那么你可以使用它直到你想出一个更符合你需求的更好的解决方案。

答案 1 :(得分:0)

您的选择包括:

(a as any).data.bar
(a.data as any).bar

或者,您可以将Something界面重新定义为

interface Something {
  name: string;
  data: any;
}

所有这些,是的,牺牲了一些类型检查。