新的一天,打卡签到。按照在 Typescript 类型推论 里的约定,咱们将文件 notes/package.json
的 version
改成 0.0.5
,而后 npm run createDir
, 这个时候,notes
目录下就会新增文件夹 0.0.5
,很简单是吧,后面章节,就忽略这些简单操做了,直接奔主题。javascript
在学以前,自觉得对象类型是用 object
来定义,其实否则,在 Typescript 的玩法里,是用接口(Interfaces)来定义。java
面向对象语言中,接口(Interfaces)是对行为的抽象(能够用事物的本质来辅助理解)。而具体如何行动由类(class)来实现(implement)(能够用事物的现象来辅助理解)。react
// interfaces.ts interface Person { name: string; age: number; } let pr: Person = { name: '胖芮', age: 30 } 复制代码
根据玩法,上面例子中定义了一个接口 Person
(行为的抽象,事物的本质),接着定义了一个变量 pr
,其类型就是 Person
(接口是类型,对象类型)。约束了定义的变量 pr
属性类型必须和接口 Person
一致。git
一般,接口首字母大写(这个能够用 react 组件名称命令规则来辅助理解)。es6
问:定义变量的属性个数比接口少能够么?多一个行不?github
// interfaces2.ts interface Person2 { name: string; age: number; } let pr2: Person2 = { name: '胖芮' } // 0.0.5/interfaces2.ts:6:5 - error TS2741: Property 'age' is missing in type '{ name: string; }' but required in type 'Person2'. // 6 let pr2: Person2 = { // 0.0.5/interfaces2.ts:3:5 // 3 age: number; // 'age' is declared here. 复制代码
// interfaces3.ts interface Person3 { name: string; age: number; } let pr3: Person3 = { name: '胖芮', age: 30, address: '杭州' } // 0.0.5/interfaces3.ts:9:5 - error TS2322: Type '{ name: string; age: number; address: string; }' is not assignable to type 'Person3'. // Object literal may only specify known properties, and 'address' does not exist in type 'Person3'. // 9 address: '杭州' 复制代码
可见,赋值的时候,多一个少一个都不行,变量的属性必须和接口的属性保持一致(前提对接口属性没作处理)。typescript
上面在对接口属性没作任何处理的状况下,赋值的时候,变量属性不能多也不能少。但是有时候咱们仍是但愿有些属性是可选的,一块儿来看看npm
// interfaces4.ts interface Person4 { name: string; age?: number; } let pr4: Person4 = { name: '胖芮' } let pr4_1: Person4 = { name: '胖芮', address: '杭州' } // 0.0.5/interfaces4.ts:12:5 - error TS2322: Type '{ name: string; address: string; }' is not assignable to type 'Person4'. // Object literal may only specify known properties, and 'address' does not exist in type 'Person4'. // 12 address: '杭州' 复制代码
可选属性是在属性后面加上 ?
,这个很容易理解(结合正则)。而对于多余属性仍然会报错(不能睁只眼闭只眼,多么正直啊)。json
难道就没别的办法了,看看下面数组
// interfaces5.ts interface Person5 { name: string; age?: number; [propName: string]: any; } let pr5: Person5 = { name: '胖芮', isMan: true, address: '杭州' } 复制代码
编译后
// build/interfaces5.js var pr5 = { name: '胖芮', isMan: true, address: '杭州' }; 复制代码
真是天无绝人之路,Typescript 我爱你,你仍是挺有人情味的嘛。
[propName: string]
定义了任意属性,属性 key 类型为 string
;any
,因此 isMan
和 address
都能经过;思考:慢着慢着,任意属性若是设为
string
,可选属性设为number
,二者有冲突么?
// interfaces6.ts interface Person6 { name: string; age?: number; [propName: string]: string; } let pr6: Person6 = { name: '胖芮', age: 30, address: '杭州' } // 0.0.5/interfaces6.ts:3:5 - error TS2411: Property 'age' of type 'number' is not assignable to string index type 'string'. // 3 age?: number; // 0.0.5/interfaces6.ts:7:5 - error TS2322: Type '{ name: string; age: number; address: string; }' is not assignable to type 'Person6'. // Property 'age' is incompatible with index signature. // Type 'number' is not assignable to type 'string'. // 7 let pr6: Person6 = { // 0.0.5/interfaces6.ts:15:5 - error TS2322: Type 'string' is not assignable to type 'number'. // 15 age: '30', // 0.0.5/interfaces6.ts:3:5 // 3 age?: number; // The expected type comes from property 'age' which is declared here on type 'Person6' 复制代码
从上面例子的报错及报错缘由中,咱们明白
age
赋值既不能为数字也不能为字符串(到底闹哪样,让不让人活了);age
的 number
类型不是任意属性的 string
的子集,因此 age
怎么赋值都不对;因此,咱们能够将任意属性改成 any
。还有一种解决方式,只不过意义不大。
// interfaces7.ts interface Person7 { name: string; age?: number | string; [propName: string]: string | number; } let pr7: Person7 = { name: '胖芮', age: 30, address: '杭州' } let pr7_1: Person7 = { name: '胖芮', age: '30', address: '杭州' } 复制代码
编译经过,经过变量的赋值,age
可为数字也可为字符串,那咱们用联合类型 number | string
,这里调整了,那任意属性也得调整,也至少得是 string | number
,固然为 any
最好不过。
说完了可选属性和任意属性,再看看另一种场景只读属性。其使用场景是对象的某些字段只在建立时被赋值,后面不可更改。
// interfaces8.ts interface Person8 { readonly name: string; age?: number | string; [propName: string]: any; } let pr8: Person8 = { name: '胖芮', age: 30, address: '杭州' } pr8.age = 18; // 永远18岁 pr8.name = '胖芮2代'; // 火影看多了 let pr8_1: Person8 = { address: '杭州' } pr8_1.age = 18; pr8_1.name = '胖芮3代'; // 0.0.5/interfaces8.ts:14:5 - error TS2540: Cannot assign to 'name' because it is a read-only property. // 14 pr8.name = '胖芮2代'; // 火影看多了 // 0.0.5/interfaces8.ts:17:5 - error TS2741: Property 'name' is missing in type '{ address: string; }' but required in type 'Person8'. // 17 let pr8_1: Person8 = { // 0.0.5/interfaces8.ts:2:14 // 2 readonly name: string; // 'name' is declared here. // 0.0.5/interfaces8.ts:22:7 - error TS2540: Cannot assign to 'name' because it is a read-only property. // 22 pr8_1.name = '胖芮3代'; 复制代码
上面例子中,咱们可看出
name
属性前加了关键字 readonly
,意指该属性只读;age
和 name
从新赋值,给 name
赋值这行报错,这是咱们但愿看到的,666;pr8_1
赋值时,可读属性 name
没有被赋值(这个错误咱们容易理解),后面才给 name
赋值,又报错,由于是它是可读属性,哪怕以前给变量赋值时没给它赋值;可见,对只读属性的约束是第一次给只读属性的对象赋值,而不是第一次给只读属性赋值。