新的一天,打卡簽到。按照在 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
賦值,又報錯,由於是它是可讀屬性,哪怕以前給變量賦值時沒給它賦值;可見,對只讀屬性的約束是第一次給只讀屬性的對象賦值,而不是第一次給只讀屬性賦值。