typescript快速上手的基礎(chǔ)知識(shí)篇
學(xué)習(xí)編程的幾個(gè)階段
1.先熟悉基礎(chǔ)知識(shí),記不住沒關(guān)系,做到有個(gè)印象,知道大概什么知識(shí)在哪一章;
2.由淺入深看示例代碼,遇到有看不懂的函數(shù),代碼寫法先去查基礎(chǔ)知識(shí),還不明白去查度娘看其他人的理解分享;
3.一直看別人的代碼,就算記住了。也很難真正成為你的知識(shí),做不到舉一反三,只是代碼的搬運(yùn)工。還得多寫代碼,在別人代碼的基礎(chǔ)上多做改動(dòng),既能動(dòng)腦思考,加深記憶,又能擴(kuò)展一些新的知識(shí)點(diǎn);
4.多討論代碼和多總結(jié)代碼,這也是知識(shí)和技術(shù)升華的過程。
與傳統(tǒng)動(dòng)態(tài)弱類型語言javascript
不同,靜態(tài)類型的typescript
在執(zhí)行前會(huì)先編譯成javascript
,因?yàn)樗鼜?qiáng)大的type
類型系統(tǒng)加持,能讓我們?cè)诰帉懘a時(shí)增加更多嚴(yán)謹(jǐn)?shù)南拗?。注意,它并不是一門全新的語言,所以并沒有增加額外的學(xué)習(xí)成本,你甚至可以將它理解為新增了類型封裝的javascript
,因此原先代碼邏輯怎么寫現(xiàn)在還是一樣的寫,至于底層編譯的事我們無需關(guān)心。
原始數(shù)據(jù)類型
typescript
與js
一樣也有原始數(shù)據(jù)類型與對(duì)象數(shù)據(jù)類型,我們先來介紹常用的原始數(shù)據(jù)類型。
1 string
let str: string = '聽風(fēng)是風(fēng)'; // 支持模板字符串 const echo = '聽風(fēng)是風(fēng)'; let s: string = `name is ${echo}`;
2 number
let num: number = 1; let notANumber: number = NaN;
3 boolean
let bool: boolean = true;
4 任意類型any
any
用于表示任意類型,如果一個(gè)變量的類型為any
,那么它可以被賦予任意類型的值,你可能會(huì)覺得any
應(yīng)該沒啥使用場(chǎng)景,但恰恰相反的是,在日常趕項(xiàng)目趕交付期間,研發(fā)同學(xué)可能沒有太多時(shí)間去做細(xì)致化類型定義,于是any
走天下,這也是typescript
又被戲稱為anyscript
的原因。
let a: any = 4; a = "4"; a = false;
這一點(diǎn)也能證明typescript
所有類型都屬于any
的子類型。
5 undefined與null
let a: undefined = undefiend; let b: null = null;
默認(rèn)情況下undefined
與null
這兩個(gè)類型屬于所有類型的子類型,也就是說你能將一個(gè)undefined
復(fù)制給一個(gè)number
的變量,但是開發(fā)中一般不推薦這么做,既然我們希望typescript
為我們添加嚴(yán)格的類型判斷,那么嚴(yán)格尊重總是有好處,你肯定也希望某種情況下undefiened
調(diào)用了某個(gè)number
的API
。
我們可以在tsconfig
配置中添加strictNullChecks:true
的開關(guān),來啟用嚴(yán)格的控制判斷,這樣undefined
或者null
就只能賦值給它們自身類型的變量,雖然這兩個(gè)類型本身用的也不多。
6 空 void
void
與any
相反,它表示沒有任意類型,比如一個(gè)函數(shù)需要返回一個(gè)字符串,你可以定義函數(shù)返回值的類型:
const fn = (): string => { return '1'; }
但假設(shè)這個(gè)函數(shù)沒有返回值,那么就可以用void
:
const fn = (): void => { console.log(1); }
但事實(shí)上開發(fā)中如果一個(gè)函數(shù)無返回,我們也不會(huì)寫void
,因?yàn)楸旧硪矝]什么意義,所以void
使用并不多。
另外即使打開了strictNullChecks
開關(guān),我們還是能將undefined
與null
賦予給void
類型的變量,即使這也沒啥意義= =。
數(shù)組類型
array
定義支持兩種寫法,一種是類型[]
,另一種是Array<類型>
,比如:
// 元素全是number類型的數(shù)組 let arr1: number[] = [1, 2, 3]; // 元素全是string類型的數(shù)組 let arr2: Array<string> = ['1', '2', '3'];
number[]
就表示這個(gè)數(shù)組的所有元素都必須是數(shù)字,包括之后你也不能往數(shù)組中添加非數(shù)字的元素,比如:
let arr: number[] = [1, 2, 3]; arr.push('聽風(fēng)是風(fēng)');// error 聽風(fēng)是風(fēng)不是number類型
那假設(shè)數(shù)組元素種類比較多,我們可以用any
來表示數(shù)組中可以出現(xiàn)任意類型,比如:
let arr: any[] = [1, 2, '3']; arr.push(undefined);
接口類型(對(duì)象類型)
我們一般用接口interface
來描述對(duì)象類型(這里的對(duì)象指{}
),比如人都有名字,性別年齡等屬性,接口即可用于對(duì)人這個(gè)類的形狀進(jìn)行抽象描述,直接看個(gè)例子:
// 我們定義了一個(gè)叫Person的接口,推薦首字母大寫 interface Person { name: string; age: number; } let echo: Person = { name: '聽風(fēng)是風(fēng)', age: 28, }
我們定義了一個(gè)接口Person
,然后變量echo
使用了接口Person
,可以看到echo
和Person
的屬性形狀需要保持一致,缺屬性或者多屬性都不行:
// 少屬性 // error Property 'age' is missing in type '{ name: string; }'. let echo: Person = { name: '聽風(fēng)是風(fēng)', } // 多屬性 let echo: Person = { name: '聽風(fēng)是風(fēng)', age: 28, gender: 'male'// Person上未指定gender:string }
1 可選屬性
接口支持使用?
讓某個(gè)屬性變?yōu)榭蛇x,女性的年齡都是秘密,某種場(chǎng)景下我們并不能拿到age
屬性,那么我們可以讓Person
的age
為可選,比如:
interface Person { name: string; age?: number; } // 缺少了age屬性,但并不會(huì)報(bào)錯(cuò) let echo: Person = { name: '西西', }
2 只讀屬性
除了限定屬性可選,我們還可以通過readonly
字段來限制某個(gè)屬性只讀,比如:
interface Person { readonly name: string; age: number; } let echo: Person = { name: '聽風(fēng)是風(fēng)', age: 28, } echo.name = '時(shí)間跳躍';// error 無法修改只讀屬性
可以看到name
添加了只讀,因此它在初始化之后就無法修改。
3 限制接口屬性范圍
比如上述代碼我們限制了名稱是字符串,也就是說任意字符串都可以,假設(shè)我們?cè)O(shè)計(jì)了一個(gè)組件,它的名稱屬性將決定組件如何展示,而且我們預(yù)定了只支持兩種模式,那么此時(shí)我們就可以更精確的來限制屬性范圍,比如:
interface P { name: 'wide'| 'narrow'; } let props:P = { name: 'wide' }
此時(shí)props
的name
字段只能是wide
或者narrow
其一,輸入其它就會(huì)報(bào)錯(cuò),這樣能很好的讓組件屬性輸入符合預(yù)期。
4 額外的任意屬性
某些情況我們希望接口能添加任意屬性,那么可以通過如下方式:
interface Person { name: string; age?: number; [propName: string]: any; } let echo: Person = { name: '聽風(fēng)是風(fēng)', age: 28, hobby: '吃西瓜', gender: 'male' }
[propName: string]: any
表示可以添加變量名為string
且值為any
類型,propName
的類型只會(huì)限制額外的屬性,假設(shè)我們將其改為[propName: number]
,那么hobby
與gender
就會(huì)報(bào)錯(cuò),而我們將其變量名改為數(shù)字則不會(huì)有問題:
interface Person { name: string; age?: number; [propName: number]: any; } let echo: Person = { name: '聽風(fēng)是風(fēng)', age: 28, 1: '吃西瓜', 2: 'male' }
但重點(diǎn)需要注意的是,假設(shè)我們定了額外的屬性,那么確定屬性以及可選屬性的類型一定得是額外屬性的子類型,比如上面string
和number
都是any
的子類型,假設(shè)我們將any
改為string
,那么age
屬性就會(huì)報(bào)錯(cuò):
interface Person { name: string; // 類型“number”的屬性“age”不能賦給字符串索引類型“string” age?: number; [propName: string]: string; } let echo: Person = { name: '聽風(fēng)是風(fēng)', age: 28, hobby: '吃西瓜', gender: 'male' }
假設(shè)我們不想定義any
來包含string
與number
,這里也可以使用聯(lián)合類型string | number
來取代any
:
interface Person { name: string; age?: number; [propName: string]: string | number; } let echo: Person = { name: '聽風(fēng)是風(fēng)', age: 28, hobby: '吃西瓜', gender: 'male' }
函數(shù)類型
javascript
中創(chuàng)建函數(shù)常用有函數(shù)聲明與函數(shù)表達(dá)式兩種形式,由于函數(shù)有輸入和輸出,所以我們需要對(duì)參數(shù)以及返回結(jié)果都做限制,我們來分別介紹兩種寫法。
1 函數(shù)聲明
function sum(x: number, y: number): number { return x + y; };
比如上述的sum
函數(shù)就接受2個(gè)類型為數(shù)字的變量x,y
,并會(huì)返回它們的和,和的類型也是數(shù)字。
在限制下我們調(diào)用函數(shù)時(shí)少傳多傳,或者傳遞的參數(shù)類型不對(duì)都會(huì)有錯(cuò)誤提示:
sum(1,'2');// error '2'的不是數(shù)字類型 sum(1);// error 需要2個(gè)參數(shù)但只傳遞了1個(gè) sum(1,2,3)// error 需要2個(gè)參數(shù)但傳遞了3個(gè)
2 函數(shù)表達(dá)式
我們將上面的代碼改為函數(shù)表達(dá)式可以是這樣:
let sum = function (x: number, y: number): number { return x + y; };
但其實(shí)這種寫法是省略了sum
類型的寫法,讓typescript
自己進(jìn)行了類型推斷,啥意思呢?我們將鼠標(biāo)放到sum
上你就能看到sum
自身的類型限制:
let sum: (x: number, y: number) => number let sum = function (x: number, y: number): number { return x + y; };
意思就是,我們將一個(gè)匿名函數(shù)賦值給了sum
,匿名函數(shù)雖然做了參數(shù)以及返回值的類型限定,但是我們沒對(duì)變量sum
做類型限定,sum
是什么類型?很顯然是函數(shù)類型,所以完整的寫法應(yīng)該是這樣:
let sum: (x: number, y: number) => number = function (x: number, y: number): number { return x + y; };
注意(x: number, y: number) => number
這一段是對(duì)于sum
這個(gè)變量類型的描述,表示它是一個(gè)函數(shù)類型,接受了哪些參數(shù),分別是什么類型,以及返回什么類型。正常我們?cè)诮涌谥忻枋鰧?duì)象某個(gè)屬性是一個(gè)函數(shù)也是相同的寫法,比如:
interface Person { name: string; age: number; canFly: () => boolean }; let echo: Person = { name: '聽風(fēng)是風(fēng)', age: 28, canFly: function () { return false } }
在接口中我們對(duì)canFly
進(jìn)行了描述,它是一個(gè)函數(shù)類型,沒有入?yún)⑶曳祷匾粋€(gè)布爾值,于是在變量echo
中我們具體實(shí)現(xiàn)了這個(gè)方法,同樣沒有入?yún)?,直接返回一個(gè)布爾值。這就相當(dāng)于函數(shù)類型說明都被提到接口中統(tǒng)一描述了,而到具體實(shí)現(xiàn)時(shí)你不用重復(fù)再限制一次。而在上面的函數(shù)表達(dá)式中,我們要么省略變量的限制讓typescript
自行推斷,要么我們手動(dòng)補(bǔ)全變量的函數(shù)類型限制,其實(shí)就是這個(gè)意思。
當(dāng)然,如果你覺得自己補(bǔ)全看著函數(shù)太長(zhǎng)了,我們也能將函數(shù)變量這一塊的描述交給接口,這樣看著就相對(duì)簡(jiǎn)潔一點(diǎn):
// 將函數(shù)變量的約束抽離出來給接口來做 interface MySum { (x: number, y: number): number } let sum: MySum = function (x: number, y: number): number { return x + y; };
3 可選參數(shù)
函數(shù)同樣支持使用?
來表示某個(gè)參數(shù)可選:
function sum(x: number, y?: number): number { return x + y; }; sum(1);
4 參數(shù)默認(rèn)值
有默認(rèn)值的參數(shù)在typescript
中會(huì)被默認(rèn)識(shí)別為可選參數(shù),畢竟有默認(rèn)值傳不傳遞都可以:
function sum(x: number, y: number = 1): number { return x + y; }; sum(1);
5 ...rest參數(shù)
在es6
中我們可以用...rest
來表示函數(shù)剩余參數(shù),這也巧妙解決了arguments
是類數(shù)組無法使用數(shù)組api
的問題,因?yàn)樵诤瘮?shù)內(nèi)rest
就是一個(gè)數(shù)組,因此我們可以用數(shù)組類型來描述rest
,比如:
function fn(a: number, ...rest: any[]) { rest.forEach((item) => console.log(item)) } fn(1, 2, 3, 4, 5);
聯(lián)合類型
很多時(shí)候,我們可能需要讓一個(gè)變量支持?jǐn)?shù)字以及字符串等多種類型,這時(shí)候就需要使用聯(lián)合類型,它使用符號(hào)|
表示,比如:
// echo的值可以是數(shù)字或者字符串 let echo: number | string = 'echo'; echo = 1;
以上代碼中的echo
可以被賦值為任意的字符串或數(shù)字類型的值。但需要注意的是,當(dāng)我們未給echo
賦予準(zhǔn)確的值,但需要訪問某個(gè)屬性或api
,此時(shí)只能訪問聯(lián)合類型共有的屬性或者方法,比如:
let echo: number | string; // 數(shù)字和字符串都支持toString方法 echo.toString(); // 報(bào)錯(cuò),Property 'toFixed' does not exist on type 'string'. echo.toFixed(1);
但假設(shè)我們給echo
賦予具體的值,此時(shí)聯(lián)合類型同樣會(huì)走類型推斷,從而讓我們能正確使用對(duì)應(yīng)類型的api
,比如:
let echo: number | string; // 此時(shí)被推斷成字符串,因此能調(diào)用字符串的api echo = '聽風(fēng)是風(fēng)'; echo.length;// 4 // 此時(shí)被推斷成數(shù)字,因此能調(diào)用數(shù)字的api echo = 1.021; echo.toFixed(2); // '1.02'
類型推斷
首先類型推斷屬于typescript
的一個(gè)概念,相當(dāng)于typescript
底層自動(dòng)會(huì)幫我們做的一件事,大家作為了解就好。
正常來說我們定義字符串是這樣,我們明確標(biāo)明了str
的類型,以及符合預(yù)期的修改str
的值:
let str: string = 'echo'; str = '聽風(fēng)是風(fēng)';
但假設(shè)我們不去定義一個(gè)變量的類型,但賦予了這個(gè)變量一個(gè)明確的值,比如一個(gè)字符串,再修改值為數(shù)字時(shí),你會(huì)發(fā)現(xiàn)報(bào)錯(cuò)了:
let str = 'echo'; str = 1; //Type '1' is not assignable to type 'string'.
這是因?yàn)?code>typescript會(huì)根據(jù)我們最初賦予的值,嘗試去推斷這個(gè)變量的類型,所以即便我們沒指定具體類型,后續(xù)也不能隨意修改值的類型,這就是所謂的類型推斷了。
上述代碼等價(jià)于:
let str: string = 'echo'; str = 1; //Type '1' is not assignable to type 'string'.
也就是說,只要你定義的文件是.ts
,就別想著在ts
文件不定義類型然后隨意賦值,這對(duì)于ts
而言肯定是不允許的。除了上文提到的幾個(gè)不常用的類型,日常開發(fā)中我們還是希望能明確標(biāo)明變量類型。
還有一種比較特殊,我指定以了一個(gè)變量但沒賦值,這時(shí)候因?yàn)闆]具體的值,所以typescript
會(huì)將這個(gè)變量推斷成any
類型,因此我們可以隨意修改這個(gè)變量的值,比如:
let echo; echo = '時(shí)間跳躍'; echo = 1; // 等同于 let echo: any; echo = '時(shí)間跳躍'; echo = 1;
類型斷言
如果說類型推斷是typescript
自動(dòng)做的類型判斷,那么類型斷言就是我們?nèi)藶槭謩?dòng)的來指定一個(gè)值的類型,它支持兩種寫法:
// <類型>變量名 <string>echo // 變量名 as 類型 echo as string
需要注意的是,在tsx
文件中只支持as
這種寫法,保險(xiǎn)起見統(tǒng)一使用as
更穩(wěn)。
為什么會(huì)有類型斷言的使用場(chǎng)景?我們來看幾個(gè)例子你就明白了。
1 聯(lián)合類型斷言場(chǎng)景
我們前面說了,當(dāng)一個(gè)變量沒具體賦值,且有聯(lián)合類型時(shí),它只能使用聯(lián)合類型共有的屬性或方法,那假設(shè)我們現(xiàn)在封裝了一個(gè)方法:
function fn(s: string | number): number { // 報(bào)錯(cuò) Property 'length' does not exist on type 'number'. return s.length; };
我們假定參數(shù)s
可能是字符串或者數(shù)字兩種類型,但是你很清楚這個(gè)方法只會(huì)接受到字符串,那我們就可以手動(dòng)指定s
的類型,比如:
function fn(s: string | number): number { return (s as string).length; };
類型斷言的目的就是我們開發(fā)者主動(dòng)的告訴typescipt
,我現(xiàn)在很清楚這個(gè)變量此時(shí)的類型是什么,從而讓typescript
不報(bào)錯(cuò),但上述編碼編譯成js
后其實(shí)也只是一個(gè)普通的返回s.length
的函數(shù),假設(shè)參數(shù)依舊傳遞了一個(gè)數(shù)字進(jìn)來,那么還是無法避免報(bào)錯(cuò)的尷尬,所以使用類型斷言時(shí)一定得謹(jǐn)慎,它只是繞過typescirpt
報(bào)錯(cuò),并沒有從根源上解決代碼兼容問題。
上述代碼通過if
來限制執(zhí)行,你會(huì)發(fā)現(xiàn)這樣實(shí)現(xiàn)其實(shí)更穩(wěn):
function fn(s: string | number): number { let length = 0; // 我們添加了判斷,也相當(dāng)于了人為對(duì)類型做了判斷,因此也不會(huì)報(bào)錯(cuò) if (typeof s === 'string') { length = s.length; }; return length; };
2 父子類斷言場(chǎng)景
ES6
支持類的定義與繼承,而當(dāng)類具有繼承關(guān)系時(shí),類型斷言也會(huì)起到作用,比如:
class P { } class P1 extends P { a: number = 1; } class P2 extends P { b: number = 2; } const fn = (s: P): boolean => { // 報(bào)錯(cuò) Property 'a' does not exist on type 'P'. if (typeof s.a === 'number') { return true; } return false; }
這里我們定義了一個(gè)父類P
,基于P
繼承得到了P1 P2
兩個(gè)類,且兩個(gè)類都有屬于自己的實(shí)例屬性,現(xiàn)在我們定義了一個(gè)比較通用的檢測(cè)P
類屬性的方法,考慮到公用型,所以類型定義我們使用P
,但在內(nèi)部實(shí)現(xiàn)中,typescript
會(huì)告訴你P
上并沒有屬性a
。這時(shí)候我們同樣可以利用斷言將類型精確到子類P1
,如下:
const fn = (s: P): boolean => { if (typeof (s as P1).a === 'number') { return true; } return false; }
當(dāng)然這也只是繞過了typescript
的檢測(cè),假設(shè)我們傳遞了一個(gè)P2
實(shí)例進(jìn)來,你會(huì)發(fā)現(xiàn)代碼會(huì)報(bào)錯(cuò),這并沒有解決根本問題。所以更好的做法還是從邏輯層間提升代碼穩(wěn)定性,比如:
const fn = (s: P): boolean => { if (s instanceof P1) { return true; } return false; }
3 將任意類型斷言為any
javascript
中存在很多原生對(duì)象,這些對(duì)象一開始就沒被添加類型,比如我們希望在全局對(duì)象window
上添加屬性就會(huì)報(bào)錯(cuò):
window.echo = 1;// error window上不存在echo的類型
這時(shí)候我們可以手動(dòng)將window
斷言為any
以解決修改屬性以及添加屬性的問題:
(window as any).echo = 1;
我們知道window
上默認(rèn)自帶一些屬性,比如name
字段默認(rèn)是一個(gè)空字符,因此在typescript
中name
也默認(rèn)被推斷成了string
類型,比如我們想將window.name
修改為數(shù)字默認(rèn)會(huì)報(bào)錯(cuò):
window.name = 1;// error 不能將1賦予給字符串類型的name
而斷言成any
可以讓我們?nèi)我庑薷?code>window上的屬性類型,這很方便但也有一定風(fēng)險(xiǎn),在實(shí)際開發(fā)中請(qǐng)謹(jǐn)慎對(duì)待。
4 將any斷言成精確的類型
在舊有代碼遷移ts
或者維護(hù)不規(guī)范的ts
代碼時(shí),我們可能會(huì)遇到因?yàn)橼s項(xiàng)目趕時(shí)間而定義比較隨意的any
類型,而你了解了這段代碼其實(shí)知道類型定義可以更為精確,重寫重構(gòu)代碼寫出完全規(guī)范的代碼之外,你能通過斷言對(duì)于模糊類型進(jìn)行補(bǔ)救,比如:
interface UserInfo { name: string; age: number; } function getUserInfo(): any { return { name: '聽風(fēng)是風(fēng)', age:28 } }; const user = getUserInfo() as UserInfo;
這樣user
后續(xù)的代碼就能清楚知道這個(gè)對(duì)象是什么類型,對(duì)應(yīng)讓代碼編寫更嚴(yán)謹(jǐn)。
5 類型斷言的限制
斷言雖然在某些場(chǎng)景很好用,但它也得滿足一些斷言場(chǎng)景,畢竟我們總不能將貓的類型斷言成魚的類型,這就不符合規(guī)范了。那么滿足什么條件才能斷言呢?先說結(jié)論,當(dāng)類型A兼容了類型B,或者B兼容了A,那么A可以斷言成B,B也能斷言成A。
什么意思?我們?cè)谏衔奶岬?,所有的類型都?code>any的子類型,也就是說any
兼容了其它所有類型,比如string
類型,因此我們可以將any as string
,也能將string as any
,這都是可以的。
我們?cè)賮砜磦€(gè)例子:
interface Person { name: string; } interface User { name: string; age: number; } let echo: User = { name: 'Tom', age: 28 }; // 注意,echo包含age,但Person類并沒有age let xixi: Person = echo;
我們定義了Person
與User
兩個(gè)接口,比較有趣的事,我們將已定義了User
類的變量echo
賦值給xixi
,而xixi
的類Person
并沒有age
屬性,但并不會(huì)報(bào)錯(cuò),而假設(shè)我們將代碼改為如下這樣,就會(huì)報(bào)錯(cuò):
interface Person { name: string; } interface User { name: string; age: number; } let echo: User = { name: 'Tom', age: 28 }; let animal: Person = { name: 'Tom', age: 28 //error age在Person中未定義 };
為啥上面正常,下面這段代碼就報(bào)錯(cuò)了,區(qū)別在哪?區(qū)別就在于上面的代碼的echo
提前定義好了User
類型,而下面的賦值只是一個(gè)單純的對(duì)象,是一個(gè)數(shù)據(jù),它無類型。
那為什么上面不報(bào)錯(cuò)呢?其實(shí)說到底還是底層類型斷言幫我們做了處理,上面的Person
與User
類型的關(guān)系你可以理解為:
interface Person { name: string; } // User繼承了Person接口,并額外添加了age interface User extends Person{ age: number; }
因此接口Person
兼容了User
(有相同屬性,屬性少的兼容屬性多的)。還記得上文父子類斷言場(chǎng)景中,我們將父類斷言成子類的操作嗎?你沒發(fā)現(xiàn)參數(shù)處我們用了屬性更少的父類P
,但事實(shí)上傳遞進(jìn)來的參數(shù)可能是接口P1
或者P2
的對(duì)象,它們的屬性都比P
接口定義的屬性要多,但是參數(shù)這里并不會(huì)報(bào)錯(cuò),而且在函數(shù)內(nèi)我們還能將P
斷言為P1
,本質(zhì)原因也是P
兼容了P1 P2
。
其實(shí)總結(jié)上面聊到的幾個(gè)使用場(chǎng)景:
- 聯(lián)合類型
string | number
斷言為string
,兩者存在兼容關(guān)系。 - 父類斷言成子類場(chǎng)景,兩者同樣存在兼容關(guān)系。
any
斷言成任意,任意類型斷言成any
,本質(zhì)上也是兼容關(guān)系。
因此,只要兩個(gè)類型存在兼容關(guān)系,不管誰兼容誰,都能相互斷言成對(duì)方的類型。
另外,我們?cè)谇懊嬲f,貓類型不能斷言成魚類型,但現(xiàn)在你會(huì)發(fā)現(xiàn)一個(gè)很有趣的事情,貓類型和any
是包含關(guān)系,而any
和魚類型同樣是包含關(guān)系,那我能不能cat as any as fish
雙重?cái)嘌灾苯訉?shí)現(xiàn)跨物種進(jìn)化呢?很明顯通過這種做法,我們能實(shí)現(xiàn)類的任意斷言,但typescript
中一般不推薦這么做,因?yàn)榇蟾怕蕰?huì)導(dǎo)致類型錯(cuò)誤....
5 類型斷言與類型聲明
我們?cè)趯?code>any斷言成其它類型講解中,為了完善類型定義模糊的代碼,我們給了一個(gè)例子,其實(shí)它還有其它的修復(fù)方法,比如不全函數(shù)的返回值的類型:
interface UserInfo { name: string; age: number; } function getUserInfo(): UserInfo { return { name: '聽風(fēng)是風(fēng)', age:28 } }; const user = getUserInfo();
除此之外,我們還能補(bǔ)全user
的類型聲明,達(dá)到相同的效果,比如:
interface UserInfo { name: string; age: number; } function getUserInfo(): any { return { name: '聽風(fēng)是風(fēng)', age: 28 } }; const user: UserInfo = getUserInfo();
單看類型斷言和類型聲明的補(bǔ)全的,你會(huì)發(fā)現(xiàn)后續(xù)使用user
完全一致,那這兩者有啥區(qū)別嗎?我們?cè)賮砜磦€(gè)例子:
interface Person { name: string; } interface User { name: string; age: number; } let echo: Person = { name: '聽風(fēng)是風(fēng)' } // echo類型是Person,沒有age屬性 let xixi = echo as User; // xixi此時(shí)能使用age屬性了 xixi.age = 27
上述代碼很明顯Person
兼容了User
,因此變量echo
我們能使用斷言將其類型由Person
轉(zhuǎn)為User
后再賦值給xixi
,之后xixi
就能賦予age
屬性了。
但假設(shè)上述代碼我們通過類型聲明來做,如下,你會(huì)發(fā)現(xiàn)報(bào)錯(cuò)了:
interface Person { name: string; } interface User { name: string; age: number; } let echo: Person = { name: '聽風(fēng)是風(fēng)' } // error Person缺少age屬性 let xixi: User = echo;
為什么報(bào)錯(cuò)?很明顯echo
的Person
類沒有age
屬性,而User
需要age
屬性,回到斷言限制的第一個(gè)例子,對(duì)比下你會(huì)發(fā)現(xiàn),我們將一個(gè)屬性更多的類賦值給一個(gè)屬性更少的類可以(后者兼容前者),反過來則會(huì)報(bào)錯(cuò)。
結(jié)合類型斷言,你會(huì)發(fā)現(xiàn)類型聲明的限制比類型斷言要嚴(yán)格的多,大致我們可以總結(jié)為:
- 若
A
需要斷言成B
,只需要A
兼容B
或者B
兼容A
都可以 - 若將
A
賦值給B
,那么一定是B
兼容A
才行(被賦值的類型屬性比作為值的類型屬性要少才行)。
以上就是兩者的區(qū)別。
內(nèi)置對(duì)象
前文我們提到,在js
中其實(shí)存在很多內(nèi)置對(duì)象,比如window、Date、RegExp
等等,你有沒有想過,假設(shè)我現(xiàn)在聲明了一個(gè)正則表達(dá)式,那我應(yīng)該添加什么類型?
其實(shí)在typescript
底層已經(jīng)幫我們封裝好了這些類型,我們可以直接使用,看部分例子:
let a: Boolean = new Boolean(1); let b: Error = new Error('報(bào)錯(cuò)啦'); let c: Date = new Date(); let d: RegExp = /'聽風(fēng)是風(fēng)'/;
可以看到通過new
一個(gè)構(gòu)造器得到的實(shí)例,它們的類型都是首字母大寫,正則比較特殊,不管是對(duì)象聲明還是new
它本身就只支持RegExp
。
再比如js
中內(nèi)置了一些DOM
以及BOM
對(duì)象,比如類數(shù)組NodeList
,事件對(duì)象Event
等,這些也能直接用于類型定義,比如:
let div:NodeList = document.querySelectorAll('.div');
更多內(nèi)置對(duì)象類型查閱MDN
,這里就不一一細(xì)說了。
總結(jié)
那么到這里,我們已經(jīng)快速過完了typescript
基礎(chǔ)內(nèi)容,準(zhǔn)備來說這些知識(shí)已經(jīng)足以支撐看懂項(xiàng)目中大部分ts
代碼了,畢竟ts
也沒有太多的額外知識(shí),更多是對(duì)于js
類型的限制,后續(xù)會(huì)再利用進(jìn)階篇補(bǔ)全剩下概念,那么到這里全文結(jié)束。
到此這篇關(guān)于typescript快速上手的基礎(chǔ)知識(shí)篇的文章就介紹到這了,更多相關(guān)typescript基礎(chǔ)知識(shí)快速上手內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
TypeScript快速學(xué)習(xí)入門基礎(chǔ)語法
TypeScript的基礎(chǔ)語法,包括變量聲明、復(fù)合類型(數(shù)組和對(duì)象)、條件控制(if-else和switch)、循環(huán)(for和while)、函數(shù)(基礎(chǔ)和箭頭函數(shù),以及可選參數(shù))、面向?qū)ο筇匦裕杜e、接口、繼承)以及模塊開發(fā)中的導(dǎo)出和導(dǎo)入2024-07-07與ChatGPT結(jié)對(duì)編程實(shí)現(xiàn)代碼詳解
這篇文章主要為大家介紹了與ChatGPT結(jié)對(duì)編寫實(shí)現(xiàn)代碼詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03TypeScript防抖節(jié)流函數(shù)示例詳解
這篇文章主要為大家介紹了TypeScript防抖節(jié)流函數(shù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08TypeScript數(shù)據(jù)結(jié)構(gòu)鏈表結(jié)構(gòu)?LinkedList教程及面試
這篇文章主要為大家介紹了TypeScript數(shù)據(jù)結(jié)構(gòu)鏈表結(jié)構(gòu)?LinkedList教程及面試,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02jsf實(shí)現(xiàn)微信小程序簡(jiǎn)潔登錄頁面(附源碼)
這篇文章主要介紹了實(shí)現(xiàn)微信小程序簡(jiǎn)潔登錄頁面?,對(duì)于正在學(xué)習(xí)的小伙伴都有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-01-01微信小程序?qū)崙?zhàn)之運(yùn)維小項(xiàng)目
這篇文章主要介紹了微信小程序?qū)崙?zhàn)之運(yùn)維小項(xiàng)目,就是利用微信小程序?qū)崿F(xiàn)了一個(gè)類似138的功能,輸入IP就可以查看IP的詳細(xì)信心,有需要的朋友可以參考借鑒,下面來一起看看吧。2017-01-01DS-SDK封裝ThreeJS的三維場(chǎng)景核心庫Viewer
這篇文章主要為大家介紹了基于DS-SDK封裝ThreeJS的三維場(chǎng)景核心庫Viewer封裝示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10Typescript?轉(zhuǎn)換類型操作索引映射類型IIMT模式學(xué)習(xí)
這篇文章主要為大家介紹了Typescript?轉(zhuǎn)換類型操作之索引映射類型IIMT模式學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07