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