在JavaScript中,像这样用字面量初始化对象的写法十分常见:
let foo = {};
foo.bar = 123;
foo.bas = 'Hello World';
但在TypeScript中,同样的写法就会报错:
let foo = {};
foo.bar = 123; // Error: Property 'bar' does not exist on type '{}'
foo.bas = 'Hello World'; // Error: Property 'bas' does not exist on type '{}'
这是因为TypeScript在解析let foo = {}这段赋值语句时,会进行“类型推断”:它会认为等号左边foo的类型即为等号右边{}的类型。由于{}本没有任何属性,因此,像上面那样给foo添加属性时就会报错。
最好的解决方案
最好的解决方案就是在为变量赋值的同时,添加属性及其对应的值:
let foo = {
bar: 123,
bas: 'Hello World'
};
这种写法也比较容易通过其他人或工具的代码审核,对后期维护也是有利的。
快速解决方案
快速解决方案采用惰性的思路,本质上是在初始化变量时忘了添加属性的做法。如果你的JavaScript项目很大,那么在迁移到TypeScript的时候,上面的做法可能会比较麻烦。此时,你可以利用TypeScript的“类型断言”机制让代码顺利通过编译:
let foo = {} as any;
foo.bar = 123;
foo.bas = 'Hello World';
折中的解决方案
当然,总是用any肯定是不好的,因为这样做其实是在想办法绕开TypeScript的类型检查。那么,折中的方案就是创建interface接口,这样的好处在于:
- 方便撰写类型文档
- TypeScript 会参与类型检查,确保类型安全
请看以下的示例:
interface Foo {
bar: number;
bas: string;
}
let foo = {} as Foo;
foo.bar = 123;
foo.bas = 'Hello World';
使用interface可以确保类型安全,比如这种情况:
interface Foo {
bar: number;
bas: string;
}
let foo = {} as Foo;
foo.bar = 123;
foo.bas = 'Hello World';
// 然后我们尝试这样做:
foo.bar = 'Hello Stranger'; // 错误:你可能把 `bas` 写成了 `bar`,不能为数字类型的属性赋值字符串
添加一个字符串索引签名方案
let foo:{
[propName: string]: any;
} = {};
foo.bar = 123;
foo.bas = 'Hello World';