【最全分析總結(jié)3W字】我是怎樣從0開始學(xué)會TypeScript的
一邊學(xué)習(xí),一邊記錄,前前后后花了至少2個月的時間,算是把TS大部分都搞明白了。
這篇文章的篇幅有點長,是我本人學(xué)習(xí)過程中的一些記錄,參考了很多優(yōu)秀博主的一些文章,以及在B站看了一些TS的視頻,把幾乎所有TS涵蓋到的基礎(chǔ)知識點都總結(jié)了下來,所以,對于想學(xué)習(xí)TS的小伙伴下來,一定一定要認認真真把這篇文章看完。
3萬字的教程,不敢說是全網(wǎng)最好,但可以說是全網(wǎng)最詳細。
對于新手入門來說是一篇非常不錯的寶藏文章,幾乎每個 TypeScript 的知識點都有詳細的講到,并且附上一些簡單的示例,通俗易懂,希望可以給想學(xué)習(xí) TS 的小伙伴帶來動力!
一、了解TypeScript1. 什么是TypeScript- TypeScript是由微軟開發(fā)的一門開源的編程語言。
- TypeScript,簡稱TS,是JavaScript的超集(以JavaScript為基礎(chǔ)構(gòu)建的語言,JS有的TS都有)。
- Typescript = Type + JavaScript(在JS基礎(chǔ)之上,為JS添加了類型支持)。
- 可以在任何支持JavaScript的平臺中執(zhí)行。
我們都知道,JavaScript是弱類型的編程語言,很多的錯誤只有在運行的時候才會被發(fā)現(xiàn),而TS在代碼編譯的時候(代碼執(zhí)行前)就可以發(fā)現(xiàn)錯誤。
3. TypeScript的特點- 支持最新的ECMAScript語法
- 在代碼編譯階段就能發(fā)現(xiàn)錯誤
- 在JS基礎(chǔ)上增加了類型支持
TypeScript | JavaScript |
編譯期發(fā)現(xiàn)錯誤 | 運行時發(fā)現(xiàn)錯誤 |
強類型語言,支持靜態(tài)和動態(tài)類型 | 弱類型語言,沒有靜態(tài)類型選項 |
支持模塊、泛型和接口 | 不支持模塊、泛型和接口 |
代碼運行時會被編譯成JavaScript代碼,瀏覽器才能識別 | 可以直接在瀏覽器使用 |
npm i g typescript
2. 驗證TS是否安裝成功tsc v
3. TypeScript初體驗- 創(chuàng)建一個TS文件,hello.ts(注意:TS文件的后綴名為 .ts),并輸入以下的內(nèi)容
- 將TS文件編譯為JS文件,在終端中輸入命令:tsc hello.ts, (此時,在同級目錄中會出現(xiàn)一個同名的JS文件)
- 執(zhí)行JS代碼:在終端中輸入命令,node hello.js,終端會輸出 hello, Echo。
每次修改代碼后,都要重復(fù)執(zhí)行兩個命令,才能運行TS代碼,我們可以直接使用tsnode工具包,直接在node.js中執(zhí)行TS代碼。
安裝命令:npm i g tsnode
使用方式:tsnode hello.ts
5. 運行TS文件的另一種方法在VSCode中安裝Code Runner擴展插件,在需要運行的ts文件中按鼠標(biāo)右鍵,選擇Run Code(快捷鍵:Ctrl+Alt+N)。
對于剛?cè)腴TTypeScript的小伙伴來說,我們可以不用安裝本地的運行環(huán)境,而是直接使用線上的 TypeScript Playground,我們就可以在瀏覽器中學(xué)習(xí)和編寫TypeScript代碼,通過配置TS Config的Target,可以設(shè)置不同的編譯目標(biāo),從而編譯生成不同的目標(biāo)代碼。
三、TypeScript類型注解1. 類型注解作用TS類型注解的作用是為變量、函數(shù)、類等添加類型信息,用于在靜態(tài)類型檢查階段檢查代碼的類型正確性。
2. 類型注解用途- 提供類型提示:類型注解使得開發(fā)人員可以清晰地知道變量的類型,編輯器能夠根據(jù)類型注解給出相應(yīng)的代碼提示,提高代碼的可讀性和可維護性。
- 靜態(tài)類型檢查:通過給變量添加類型注解,在編譯階段可以對代碼進行靜態(tài)類型檢查。它會檢查變量的類型是否符合預(yù)期的類型,并發(fā)現(xiàn)潛在的類型錯誤。
- 函數(shù)參數(shù)類型檢查:類型注解可以幫助開發(fā)人員在編寫函數(shù)時明確參數(shù)的類型,并在調(diào)用函數(shù)時進行參數(shù)類型檢查。這樣可以避免因參數(shù)類型不匹配引發(fā)的潛在錯誤。
- 對象屬性類型約束:通過類型注解,可以約束對象的屬性類型,確保對象的屬性符合特定的類型要求。
例如,上述代碼中的 : number 就是類型注解。 約定變量num的類型為number(數(shù)值類型)。
3. 類型注解注意事項- 約定了什么類型,就只能給變量賦值該類型的值,否則,就會報錯。
例如,我們將變量num的值123,重新賦值為字符串的“456”,此時我們就可以看到編輯器的錯誤提示:不能將類型“string”分配給類型“number”。
- 類型注解只在編譯階段起作用,并不會影響運行時的行為。 在編譯后的 JavaScript 代碼中,類型注解會被編譯器忽略。
我們可以將TS中常用的基礎(chǔ)類型分為兩類:
- JS已有的類型
- TS新增的類型
JS已有的類型,我們又可以分為兩類:
- 原始數(shù)據(jù)類型:number、string、boolean、null、undefined、symbol(ES6中的新類型)、bigint(ES10中的新類型)。
- 對象類型:object(包括數(shù)組、對象、函數(shù)等對象)。
TS新增的類型:any、void、自定義類型(類型別名)、聯(lián)合類型、接口、元組、字面量類型、枚舉等。
1.1. 數(shù)值(number)和JS一樣,TS里的所有數(shù)字都是浮點數(shù)。 這些浮點數(shù)的類型是 number。 除了支持十進制和十六進制字面量,TS還支持ECMAScript 2015中引入的二進制和八進制字面量。
在TS中,使用 number 來定義數(shù)值類型:
編譯結(jié)果:
在TS中,使用 boolean 來定義布爾值類型:
編譯結(jié)果:
在TS中,使用 string 來定義字符串類型:
在TS中,字符串的表現(xiàn)形式主要有以下三種方式:
- 使用單引號( &39; )
- 使用雙引號( &34; )
- 使用模板字符串,它可以定義多行文本和內(nèi)嵌表達式。這種字符串是被反引號包圍( ` ),并且以 ${ expr } 這種形式嵌入表達式
編譯結(jié)果:
null 和 undefined 是所有類型的子類型,默認情況下,可以把null 和 undefined賦值給其他類型。
注意:如果你將 tsconfig.json 文件中的 strictNullChecks 選項設(shè)置為 false,下面這種操作不會報錯,不過盡量不要這么做。
編譯結(jié)果:
注意:如果你在 tsconfig.json 文件中指定了“strictNullChecks:true”,null 和 undefined 只能賦值給 void 和它們各自的類型。
下面這種情況會報錯:
symbol 是ES6新增的一種基本數(shù)據(jù)類型,Symbol()函數(shù) 會返回 symbol 類型的值,每個從 Symbol()函數(shù) 返回的 symbol 的值都是唯一的。
上面的代碼創(chuàng)建了三個新的 symbol 類型,但是注意的是,每個從 Symbol()函數(shù) 返回的值都是唯一的。
此時,如果我們在控制臺打印下面的代碼,兩者并不相等。
bigint 是ES10新增的一種基本數(shù)據(jù)類型,在JS中,可以用 Number 表示的最大整數(shù)為 2^53 1,可以寫為 Number.MAX_SAFE_INTEGER。如果超過了這個界限,那么就可以用 BigInt 來表示,它可以表示任意大的整數(shù)。
在一個整數(shù)字面量后面加 n 的方式定義一個 bigint,或者調(diào)用函數(shù) BigInt()。
- 在JS中,null 表示“什么都沒有”,而 undefined 是一個沒有設(shè)置值的變量
- 用 typeof 檢測 null,返回 object;typeof 一個沒有值的變量會返回 undefined
- null 是一個只有一個值的特殊類型,表示一個空對象的引用
- null 和 undefined 是其它任何類型(包括void)的子類型,可以賦值給其它類型,如數(shù)字類型,此時,賦值后的類型會變成 null 或 undefined。而在TS中啟用嚴格的空校驗(strictNullChecks)特性,就可以使得 null 和 undefined 只能被賦值給 void 或本身對應(yīng)的類型
- number 和 bigint 都可以表示數(shù)字,但是兩者不能進行相互轉(zhuǎn)換
- 僅在值大于 2^53 1時,才使用 bigint,否則盡量使用 number
- 用 typeof 檢測 bigint 對象時,返回 bigint,用 typeof 檢測 number,返回 number
數(shù)組類型的寫法有兩種:
- 在類型后面加上 [],例如 number[]
- 使用數(shù)組泛型 <>,例如 Array
推薦使用第一種寫法。
注意:
- 如果我們定義了一個number類型的數(shù)組,此時數(shù)組的項中就不能出現(xiàn)其它的類型。
- 如果我們希望數(shù)組中既有number類型,又有string類型,此時我們可以用聯(lián)合類型來寫(關(guān)于聯(lián)合類型,后面會詳細講到)。
上面的代碼,表示的是,定義一個arr數(shù)組,這個數(shù)組中可以出現(xiàn) number 或者 string 類型的元素。
上面的代碼,arr1 和 arr2 都表示即可以是number類型,又可以是string[],加了小括號和不加小括號,含義不同。
1.8.2. 函數(shù)類型函數(shù)類型實際上指的是:函數(shù)參數(shù)和返回值的類型。
為函數(shù)指定類型的兩種方式:
- 單獨指定參數(shù)、返回值的類型
- 同時指定參數(shù)、返回值的類型
在JS中,有兩種常見的定義函數(shù)的方式:
- 函數(shù)聲明
- 函數(shù)表達式
注意:不要把ES6中的 => 和 TypeScript 中的 =>混淆了。
在ES6中,=>叫做箭頭函數(shù)。而在 TypeScript 的類型定義中,=>用來表示函數(shù)的定義,左邊是輸入類型,需要用括號括起來,右邊是輸出類型。
1.8.2.3. 函數(shù)沒有返回值如果函數(shù)沒有返回值,那么,函數(shù)返回值類型為:void。
使用函數(shù)實現(xiàn)某個功能時,參數(shù)可以傳也可以不傳,這種情況下,在給函數(shù)參數(shù)指定類型時,就用到可選參數(shù)了。
可選參數(shù)使用問號( ? )標(biāo)記,表示該參數(shù)可以省略。
上面的代碼中,我們在第二個參數(shù) greeting 的后面加了個問號,表示在調(diào)用 greet() 函數(shù)時,該參數(shù)可傳可不傳。
注意:可選參數(shù)只能出現(xiàn)在參數(shù)列表的最后面,也就是說,可選參數(shù)后面不能再出現(xiàn)必選參數(shù)。
錯誤演示:下面代碼中,我們把第一個參數(shù)改為可選的,第二個參數(shù)改為必選的,然后將鼠標(biāo)移到必選參數(shù)上面,可以看到錯誤提示:“必選參數(shù)不能位于可選參數(shù)后”。
在ES6中,允許給函數(shù)的參數(shù)添加默認值,而TypeScript會將添加了默認值的參數(shù)識別為可選參數(shù)。
默認參數(shù)使用等號(=)賦予默認值。
注意:與可選參數(shù)不同的是,帶默認值的參數(shù)不需要放在必選參數(shù)的后面。如果帶默認值的參數(shù)出現(xiàn)在必選參數(shù)的前面,我們在調(diào)用函數(shù)時,必須明確的傳入 undefined 值來獲得默認值。
使用三個點( ... )前綴和參數(shù)名來定義剩余參數(shù)。
剩余參數(shù)允許我們將不確定數(shù)量的參數(shù)表示為一個數(shù)組。
注意:剩余參數(shù)必須是函數(shù)參數(shù)列表中的最后一個參數(shù)。
1.8.2.7. 函數(shù)重載函數(shù)重載允許我們?yōu)橥粋€函數(shù)提供多個函數(shù)類型定義,以便在不同的參數(shù)類型或返回值類型下進行不同的處理。
例如,我們現(xiàn)在需要實現(xiàn)一個函數(shù),需求是:輸入數(shù)字123,輸出反轉(zhuǎn)的數(shù)字321,輸入字符串&34;hello&34;,輸出反轉(zhuǎn)的字符串&34;olleh&34;。
利用聯(lián)合類型,我們可以這么實現(xiàn):
然后這樣會有一個問題,就是輸出的類型不能準(zhǔn)確的知道,我們想輸入為數(shù)字的時候,輸出的類型應(yīng)該也為數(shù)值類型,輸入為字符串的時候,輸出類型應(yīng)該也為字符串類型。
這時,我們可以用重載定義多個reserve的函數(shù)類型:
上述代碼中,第12行是函數(shù)定義,第39行是函數(shù)實現(xiàn)。第11行代碼,我們調(diào)用reverse函數(shù),并傳入數(shù)值123,使用typeof檢測類型為number,第12行代碼,我們調(diào)用reverse函數(shù),并傳入字符串&34;hello&34;,使用typeof檢測類型為string,這樣我們利用函數(shù)重載就實現(xiàn)了輸入為什么類型,輸出應(yīng)該也是什么類型。
1.8.3. 對象類型JS中的對象是由屬性和方法構(gòu)成的,而TS中對象的類型就是在描述對象的結(jié)構(gòu)(有什么類型的屬性和方法)。
1.8.3.1. 定義對象類型- 使用花括號( {} )來定義對象類型,屬性采用 屬性名: 類型 的形式;方法采用 方法名(): 返回值類型 的形式。
- 如果方法有參數(shù),就在方法名后面的小括號中指定參數(shù)類型(比如:greet(name: string): void)。
- 在一行代碼中指定對象的多個屬性類型時,使用分號( ; )來分隔。
- 如果一行代碼只指定一個屬性類型(通過換行來分隔多個屬性類型),可以去掉分號( ; )。
- 方法的類型也可以使用箭頭函數(shù)形式,比如:{ sayHi: () => void }。
上面的代碼,也可以寫成下面這種形式:
對象類型中的屬性或方法可以是可選的,使用問號( ? )標(biāo)記。
可選屬性表示該屬性可以存在,也可以不存在。
比如,我們在使用axios({...})時,如果發(fā)送GET請求,method屬性就可以省略。
對象的屬性也可以是只讀的,使用 readonly 關(guān)鍵字標(biāo)記。
只讀屬性表示該屬性的值在創(chuàng)建后就不能被修改。
在 TypeScript 中,元組(Tuple)是一種特殊的數(shù)組類型,它允許 存儲具有固定數(shù)量和特定類型順序的元素。
聲明一個元組的語法是在類型注解中使用方括號 [] ,并使用逗號分隔元素的類型。
例如,下面是一個包含兩個元素的元組:
在上述示例中,我們聲明了一個名為 tuple 的變量,它被注解為一個元組類型 [string, number]。我們可以將一個包含兩個元素的數(shù)組賦值給 tuple,其中第一個元素是一個字符串,第二個元素是一個數(shù)字。
2.2. 元組的特點- 元組可以包含多個不同類型的元素,但每個元素的類型和順序是固定的。
- 元組的長度是固定的,在創(chuàng)建元組時必須指定元素的數(shù)量。
- 可以通過索引訪問元組中的元素,索引從 0 開始。
- 元組中的每個元素可以具有不同的類型注解。
- 當(dāng)訪問元組中的元素時,會根據(jù)其類型注解提供相關(guān)的類型檢查和智能提示。
下面是一些操作元組的示例:
注意:當(dāng)訪問元組中的元素以及進行元素的賦值時,要確保索引和類型注解的一致性,否則可能會導(dǎo)致類型錯誤。
2.3. 元組類型的解構(gòu)賦值在 TypeScript 中,可以使用解構(gòu)賦值語法來從元組中提取和賦值元素。
下面是一個簡單的示例,展示了如何使用解構(gòu)賦值從元組中獲取各個元素:
在上述示例中,我們首先聲明了一個元組 tuple,其中包含一個字符串類型的元素和一個數(shù)值類型的元素。接著,我們使用解構(gòu)賦值語法將元組中的元素分別賦值給變量 str 和 num。
通過解構(gòu)賦值,我們可以直接使用對應(yīng)位置的變量來獲取元組中的元素值,而不需要通過索引訪問。這樣可以以一種簡潔、語義明確的方式從元組中解構(gòu)得到各個元素。
解構(gòu)賦值還支持忽略某些元素,或者只提取部分元素。
例如,如果只想獲取元組中的第一個元素,可以使用以下方式:
在上述示例中,我們只聲明了一個變量 str,而忽略了后面的元素。通過解構(gòu)賦值只獲取所需元素,可以簡化代碼并提高可讀性。
另外,解構(gòu)賦值還支持使用默認值。
當(dāng)從元組中解構(gòu)一個不存在的元素時,可以提供一個默認值作為備選值。例如:
在上述示例中,我們聲明了一個帶有可選的數(shù)字元素的元組 tuple,但是沒有給出對應(yīng)的數(shù)字值。在解構(gòu)賦值時,如果元組中缺少對應(yīng)的元素,就會使用默認值 undefined,這里我們將默認值設(shè)置為 26。
總而言之,使用解構(gòu)賦值可以輕松地從元組中提取和賦值元素,使得代碼更加簡潔和可讀。它是一種方便的語法,特別適用于處理具有固定結(jié)構(gòu)的數(shù)據(jù)。
注意:在解構(gòu)賦值時,如果解構(gòu)數(shù)組元素的個數(shù)超過元組中元素的個數(shù),會出現(xiàn)錯誤。
在上述示例中,我們解構(gòu)時新增了一個 sex 變量,但元組的長度為 2,在索引 &34;2&34; 處沒有元素。
在 TypeScript 中,可以使用問號 ? 來將元素定義為可選的,以表示元組中某些位置的元素是可選的。
在上述示例中,我們定義了一個元組 tuple,該元組有兩個元素,第一個是一個字符串類型的元素,而第二個是一個可選的數(shù)值類型的元素。當(dāng)我們只提供第一個元素時,第二個元素會被默認設(shè)置為 undefined。然后,我們更新了元組的值,提供了第二個元素的值。此時,元組中的兩個元素都有具體的值。
注意,當(dāng)一個元組中包含一個可選元素時,該元素可以存在或不存在,但是順序必須與元組類型定義一致。在解構(gòu)賦值時,可以使用默認值來處理可選元素的缺失情況。
在上述示例中,我們使用解構(gòu)賦值將元組中的元素分別賦值給變量 str 和 num。由于元組只提供了一個元素,沒有提供可選的第二個元素,所以 num 的值將使用默認值 26。
通過使用可選元素,可以更靈活地定義元組類型,允許元組中特定位置的元素是可選的。這樣,我們可以在處理數(shù)據(jù)時更好地適應(yīng)不完整或可變的情況。
2.5. 元組類型的剩余元素在 TypeScript 中,可以使用剩余元素(Rest Elements)來表示元組中剩余的元素,即將剩余的元素放入一個數(shù)組中。
在上述示例中,我們定義了一個元組 tuple,包含一個字符串元素、一個數(shù)字元素,以及剩余元素使用剩余元素語法 ... 定義的布爾類型數(shù)組。在創(chuàng)建元組時,我們提供了多個布爾類型的元素,它們會被放入一個數(shù)組并作為剩余元素。這樣,元組中除了前兩個元素以外的其他元素都會被放入數(shù)組中,并以數(shù)組的形式表示。
在上述示例中,我們使用解構(gòu)賦值從元組中提取出各個元素。通過使用 ...boolArr,我們將剩余的布爾類型元素提取到名為 boolArr 的數(shù)組中。
使用剩余元素可以處理元組中數(shù)量不確定的元素,可以更靈活地處理和操作這些元素。它提供了一種方便的方式來處理由不固定數(shù)量的元素組成的結(jié)構(gòu)數(shù)據(jù)。
2.6. 只讀的元組類型在 TypeScript 中,可以使用 readonly 修飾符來創(chuàng)建只讀的元組類型,即元組中的元素不可被修改。
在上述示例中,我們使用 readonly 修飾符將 tuple 聲明為只讀的元組類型。這意味著在運行時,我們無法修改元組中的元素的值。
嘗試對 tuple 進行賦值或調(diào)用修改元素的方法(如 push)時,TypeScript 編譯器會報錯,因為元組被聲明為只讀,無法被修改。
只讀的元組類型在某些場景下非常有用,特別是當(dāng)希望確保元組中的數(shù)據(jù)不會被意外修改時。它提供了一種強制保護元組數(shù)據(jù)不可變性的機制。
3. 字面量類型當(dāng)我們在 TypeScript 中使用字面量類型,我們可以明確指定變量只能取特定的字面量值,而不是其他可能性。這樣可以在編譯時捕獲潛在的錯誤,并提供更好的類型推斷和類型檢查支持。
在 TypeScript 中,可以使用多種類型的字面量進行類型定義,包括字符串字面量類型、數(shù)字字面量類型、布爾字面量類型和符號字面量類型。
3.1. 字符串字面量類型使用字符串字面量表示的類型,只能取特定的字符串值。
使用數(shù)字字面量表示的類型,只能取特定的數(shù)字值。
使用布爾字面量表示的類型,只能取特定的布爾值。
使用符號字面量表示的類型,只能取特定的符號值。
字面量類型不僅可以用于變量的定義,還可以用于函數(shù)的參數(shù)、返回值、對象屬性等地方。通過使用字面量類型,我們可以在編寫代碼時明確指定特定的取值范圍,提高代碼的可讀性和可維護性。
需要注意的是,字面量類型具有一個特殊的用途,即與聯(lián)合類型結(jié)合使用,以實現(xiàn)更精確的類型約束。例如,聯(lián)合類型 string | number 表示可以是字符串或數(shù)字類型的值,而字面量類型 &34;success&34; | &34;error&34; 表示只能是字符串 &34;success&34; 或 &34;error&34;,它們可以一起使用來實現(xiàn)更精確的類型定義。
在上述示例中,函數(shù) move 的參數(shù) direction 的類型被指定為 &34;up&34; | &34;right&34; | &34;down&34; | &34;left&34;,這意味著參數(shù) direction 只能接受這四個特定的值。
3.6. 函數(shù)返回值中的字面量類型在上述示例中,函數(shù) getMove 的返回值被指定為 &34;up&34; | &34;right&34; | &34;down&34; | &34;left&34;,這表示函數(shù)的返回值只能是這四個特定的值之一。
3.7. 對象屬性中的字面量類型在上述示例中,Options 接口中的 mode 屬性的類型被指定為 &34;light&34; | &34;dark&34;,size 屬性的類型被指定為 &34;small&34; | &34;medium&34; | &34;large&34;,這意味著對象 config 的 mode 屬性只能是其中一個值,size 屬性也只能是其中一個值。
3.8. let 和 const 分析3.8.1 let 聲明的字面量類型在上述示例中,我們使用 let 關(guān)鍵字聲明了變量 direction,并將其類型指定為 &34;Up&34; | &34;Right&34; | &34;Down&34; | &34;Left&34;,因此 direction 只能取值為 &34;Up&34; 或 &34;Right&34; 或 &34;Down&34; 或 &34;Left&34; 這四個特定值中的其中一個。
3.8.2 const 聲明的字面量類型在上述示例中,我們使用 const 關(guān)鍵字聲明了常量 size,并將其類型指定為 &34;small&34; | &34;medium&34; | &34;large&34;。由于使用了 const,size 是一個只讀的常量,且初始值為 &34;medium&34;。因此,size 的值將永遠是 &34;medium&34;,不能被重新賦值。
使用 let 和 const 關(guān)鍵字來聲明變量和常量時,可以配合字面量類型提供更具體和可靠的類型約束。
注意:const 聲明的常量在聲明時必須被初始化,并且一旦初始化后,其值將不能被修改。而 let 聲明的變量可以在后續(xù)代碼中被重新賦值。
4. 枚舉(Enum)枚舉(Enum)是一種用于定義一組命名常量的數(shù)據(jù)結(jié)構(gòu)。
4.1. 基本枚舉在上述示例中,我們定義了一個名為 Direction 的枚舉,其中列出了 Up、Down、Left 和 Right 四個枚舉成員。默認情況下,枚舉成員的值從 0 開始自動遞增,因此 Direction.Up 的值為 0。我們可以使用枚舉成員來聲明變量,并進行比較、打印等操作。
4.2. 數(shù)字枚舉在默認情況下,數(shù)字枚舉的成員從 0 開始自動遞增。
4.2.1. 默認遞增的數(shù)字枚舉在上述示例中,我們定義了一個名為 Direction 的枚舉,其中列出了 Up、Down、Left 和 Right 四個枚舉成員。默認情況下,枚舉成員的值從 0 開始自動遞增,因此 Direction.Up 的值是 0,Direction.Down 的值是 1,Direction.Left 的值是 2,Direction.Right 的值是 3。
4.2.2. 手動賦值的數(shù)字枚舉在手動賦值的數(shù)字枚舉中,可以為每個枚舉成員手動指定一個特定的值。手動賦值的數(shù)字枚舉可以使用任意合法的數(shù)字作為成員的值。
在上述示例中,Direction.Up 被賦值為 2,Direction.Down 被賦值為 4,Direction.Left 被賦值為 6,Direction.Right 被賦值為 8。
4.2.3. 計算成員的數(shù)字枚舉在數(shù)字枚舉中,可以使用計算表達式作為成員的值。
在上述示例中,我們使用加法、減法、乘法和除法運算符來計算成員的值。在編譯時,這些計算表達式會被求值為結(jié)果值并成為實際的枚舉成員的值。
4.3. 常量枚舉常量枚舉(const enum)是一種特殊類型的枚舉,它在編譯時被刪除,并且只保留枚舉成員的值作為常量。常量枚舉提供了一種更輕量級的方式來使用枚舉,可以用于在編譯期間替換枚舉成員的值。
4.3.1. 常量枚舉的定義在定義常量枚舉時,需要使用 const 關(guān)鍵字和 enum 關(guān)鍵字的組合。常量枚舉不能有計算成員。
在 TypeScript 中,字符串枚舉是一種特殊類型的枚舉,其中每個成員都用字符串字面量進行初始化。
在上述示例中,我們定義了一個名為 Direction 的字符串枚舉。其中的成員 Up 使用字符串字面量 &34;UP&34; 進行初始化,成員 Down 使用字符串字面量 &34;DOWN&34; 進行初始化,成員 Left 使用字符串字面量 &34;LEFT&34; 進行初始化,成員 Right 使用字符串字面量 &34;RIGHT&34; 進行初始化。我們可以通過直接訪問枚舉成員來獲得其對應(yīng)的字符串值。
字符串枚舉的特點:
- 明確的字符串值:每個字符串枚舉成員都具有明確的字符串值,可更好地描述其含義和用途。
- 代碼可讀性:由于成員的值直接使用字符串字面量,因此代碼更加清晰、易讀。
- 保留字符串字面量:使用字符串枚舉可以在編譯后保留字符串字面量,而不是轉(zhuǎn)換為數(shù)值或其他類型。
- 可用于反向映射:字符串枚舉可以支持從枚舉值到枚舉名的反向映射。
外部枚舉(ambient enum)是一種定義在外部代碼(如聲明文件)中的枚舉。外部枚舉通常用于描述已存在的枚舉類型的形狀,而不是為了創(chuàng)建一個具體的 JavaScript 對象。
外部枚舉的定義不會在編譯時生成任何實際的 JavaScript 代碼,它只用于類型檢查。
在上述示例中,我們使用 declare 關(guān)鍵字來定義了一個外部枚舉 HttpStatusCode。它描述了一些常見的 HTTP 狀態(tài)碼。其中的成員 OK 和 BadRequest 和 NotFound 指定了具體的數(shù)值,分別為 200,400 和 404,成員 Unauthorized 沒有顯式指定值,它會根據(jù)前一個成員的值自動遞增,因此值為 401。
在使用外部枚舉時,我們可以像使用普通枚舉一樣,訪問它的成員并獲得相應(yīng)的值。在上述示例中,我們將 HttpStatusCode.OK 賦值給變量 code,然后將變量 code 的值打印出來,得到的結(jié)果是 200。
注意:當(dāng)使用外部枚舉時,我們必須使用 declare 來聲明它,以告訴 TypeScript 編譯器這是一個外部定義的枚舉。此外,外部枚舉的定義通常是在一個聲明文件中(以 .d.ts 結(jié)尾),以便在與現(xiàn)有 JavaScript 庫或框架進行交互時提供類型信息。
總結(jié)起來,外部枚舉是 TypeScript 中一種在外部代碼中定義的枚舉,用于描述已存在的枚舉類型的形狀。外部枚舉的定義通常只用于類型檢查,并不會生成實際的 JavaScript 代碼。它在與現(xiàn)有 JavaScript 庫或框架進行交互時提供類型信息。
4.6. 異構(gòu)枚舉異構(gòu)枚舉(heterogeneous enum)是一種允許枚舉成員的值具有不同類型的枚舉。
通常情況下,枚舉中的成員的值應(yīng)該是相同類型的。但是異構(gòu)枚舉允許在同一個枚舉中使用不同類型的值,包括字符串、數(shù)字和其他類型。
在上述示例中,我們定義了一個名為 Status 的異構(gòu)枚舉。其中的成員 Active 的值是一個數(shù)字,值為 1。成員 Pending 沒有顯式指定值,它的值會根據(jù)前一個成員的值自動遞增,因此值為 2。成員 Inactive 的值是一個字符串,值為 &34;inactive&34;。成員 OnHold 的值是一個字符串,值為 &34;on hold&34;。
在訪問異構(gòu)枚舉的成員時,將得到其對應(yīng)的值。在上述示例中,我們分別打印了每個異構(gòu)枚舉成員的值,并相應(yīng)地獲得了不同類型的結(jié)果。
異構(gòu)枚舉的優(yōu)勢在于允許在一組相關(guān)的枚舉中使用不同類型的值。這在某些特定情況下可能很有用,例如需要表示不同種類的狀態(tài)或類型時。
注意:在異構(gòu)枚舉中,具有數(shù)字字面量值的成員會根據(jù)前一個成員的值自動遞增,而具有字符串字面量值的成員不會自動遞增。同時,在異構(gòu)枚舉中,沒有初始化值的成員會根據(jù)前一個成員的值自動遞增。
4.7. 反向映射反向映射(reverse mapping)是指枚舉成員不僅可以通過名稱訪問值,而且可以通過值訪問名稱。 這意味著可以根據(jù)枚舉的值獲取到對應(yīng)的枚舉成員名稱。
在上述示例中,我們定義了一個名為 Direction 的枚舉,其中的成員分別使用數(shù)字進行初始化。我們將 Direction.Right 的值賦給變量 rightValue,然后使用 Direction[rightValue] 獲取到對應(yīng)的枚舉成員名稱,將結(jié)果賦給變量 rightName。
在打印出變量 rightValue 和 rightName 的值后,我們得到的結(jié)果是 4 和 Right。這就是反向映射的效果,根據(jù)枚舉的值可以獲取到對應(yīng)的枚舉成員名稱。
注意:反向映射只在數(shù)字枚舉中有效,而不適用于字符串枚舉。 字符串枚舉的成員值雖然可以是字符串字面量,但在 JavaScript 中無法實現(xiàn)反向映射。
4.8. 運行時的枚舉運行時的枚舉(runtime enum)是指在 JavaScript 運行時可訪問和操作的枚舉。
TypeScript 編譯器在編譯過程中,會將枚舉類型轉(zhuǎn)換為實際的 JavaScript 對象。這些對象在運行時仍然保留了枚舉的結(jié)構(gòu)和值,以便能夠通過它們來進行運行時的枚舉操作。
在上述示例中,我們定義了一個名為 Fruit 的枚舉,其中包含了三個成員 Apple、Orange 和 Banana。然后我們定義了一個函數(shù) getFruitName,它接受一個 Fruit 類型的參數(shù),根據(jù)傳入的枚舉值返回對應(yīng)的水果名稱。
通過運行 getFruitName 函數(shù)并傳入不同的枚舉值,我們可以在控制臺上看到輸出的結(jié)果,它們是根據(jù)傳入的枚舉值返回的相應(yīng)水果名稱。
注意:當(dāng)使用運行時枚舉時,由于枚舉的成員值實際上是數(shù)字(默認從 0 開始遞增),因此進行比較時需要使用嚴格相等運算符 ===。
4.9. 聯(lián)合枚舉聯(lián)合枚舉(union enum)是指一個枚舉類型可以包含多個不同的枚舉成員的組合。每個成員可以具有不同的值和類型。
在上述示例中,我們定義了兩個枚舉 Shape 和 Color。Shape 枚舉表示不同的形狀,Color 枚舉表示不同的顏色。然后我們定義了一個類型別名 ShapeColor,它是 Shape 枚舉成員和 Color 枚舉成員的聯(lián)合。接著,我們定義了一個函數(shù) drawShape,它接受一個 ShapeColor 類型的參數(shù) shape。根據(jù)傳入的參數(shù)值進行不同的分支邏輯處理,并輸出相應(yīng)的消息。通過調(diào)用 drawShape 函數(shù)并傳入不同的值,我們可以根據(jù)傳入的參數(shù)值來繪制不同的形狀或填充不同的顏色。
聯(lián)合枚舉使得我們能夠在一個類型中組合多個不同的枚舉成員,以表示更復(fù)雜的類型。這可以讓 TypeScript 的類型系統(tǒng)提供更精確的類型檢查和推斷,以確保代碼的正確性。
注意:聯(lián)合枚舉的使用是通過定義類型別名或接口來實現(xiàn)的。 通過將不同枚舉成員組合在一起,可以創(chuàng)建復(fù)合類型,提供更靈活的數(shù)據(jù)表示。
5. any類型在 TypeScript 中,any 類型表示一個動態(tài)類型,它可以接受任何類型的值。使用 any 類型時,TypeScript 編譯器將不會對值進行類型檢查,允許你在編譯期繞過類型系統(tǒng)的限制。
如果是一個普通類型,在賦值過程中改變類型是不被允許的。
如果是 any 類型,則允許被賦值為任意類型。
以下兩種情況,隱式具有 any 類型:
- 聲明變量不提供類型也不提供默認值。
- 函數(shù)參數(shù)不加類型。
注意:在開發(fā)過程中應(yīng)盡量避免過度使用 any 類型,以充分利用 TypeScript 的類型系統(tǒng)來提供更好的類型安全性和代碼可維護性。
五、接口(interface)1. 什么是接口在 TypeScript 中,接口(Interface)是一種用來定義對象的結(jié)構(gòu)和行為的類型。通過接口,我們可以定義對象應(yīng)該有哪些屬性、屬性的類型以及方法。
接口提供了一種約束和規(guī)范,使得我們可以在代碼中定義和使用特定的數(shù)據(jù)結(jié)構(gòu)。
2. 定義接口- 使用關(guān)鍵字 interface 來定義接口。
- 聲明接口后,直接使用接口名稱作為變量的類型。
- 方法的定義和函數(shù)的定義類似,包括參數(shù)和返回值類型。
- 接口一般首字母大寫。有的編程語言中建議接口的名稱加上前綴 “ I” ,“ 。”
上面的代碼中,我們定義了一個接口 Person,接著定義了一個變量 jerry,它的類型是 Person。這樣,我們就約束了 jerry 的形狀必須和接口 Person 一致。
注意:定義的變量比接口少了一些屬性不允許的。
下面是一段錯誤的代碼演示:我們定義了一個接口 Person,里面有name,age2個屬性,以及sayHi方法,接著定義了一個變量 jerry,它的類型是 Person,但是我們只給屬性name和age賦值,所以會報錯。
當(dāng)然,定義的變量比接口多了一些屬性也是不允許的。
也就是說,在賦值的時候,變量的形狀必須和接口的形狀保持一致。
3. 接口(interface)和類型別名(type)的區(qū)別- 相同點:都可以用于定義對象的結(jié)構(gòu)和類型。
- 不同點:
- 接口更適合用于描述真實存在的對象,而類型別名更適合用于定義復(fù)雜的類型。 接口可以被其他對象實現(xiàn),而類型別名只是給類型起了一個別名。
在 TypeScript 中,接口是可以相互繼承的,也就是說:一個接口可以從另一個接口中繼承屬性和方法的定義(通過繼承實現(xiàn)復(fù)用)。 接口的繼承可以通過使用關(guān)鍵字 extends 實現(xiàn)。
接口繼承的語法格式如下:
通過繼承,子接口可以獲得父接口中定義的屬性和方法,并可以在自身接口中添加新的屬性和方法。
下面是一個簡單的例子,展示了接口繼承的用法:
在上面的例子中,使用 extends 關(guān)鍵字實現(xiàn)了接口 Circle 繼承 Shape。繼承后,Circle 就有了 Shape 中的 color 屬性,以及自身的 radius 屬性以及 getArea() 方法。
5. 接口的可選屬性帶有可選屬性的接口與普通的接口定義差不多,只是在可選屬性名字定義的后面加一個 ? 符號。
上面的例子中,Person 接口中的 age 屬性是可選的,我們定義了 person1 和 person2 兩個對象,類型都是Person,其中,person1 對象中沒有 age 屬性,而 person2 對象中包含了 age 屬性。
可選屬性的好處有2個:
- 可以對可能存在的屬性進行預(yù)定義
- 可以捕獲引用了不存在的屬性時的錯誤
例如,我們故意將 person2 對象中的 age 屬性名寫錯,就會得到一個錯誤的提示。
有時候我們希望某些屬性在對象創(chuàng)建后不能被修改,可以將這些屬性聲明為只讀屬性。
通過在屬性名稱前面加上 readonly 關(guān)鍵字,就可以將屬性設(shè)置為只讀。
例如,下面的例子中,聲明了一個名稱為 Point2D 的接口,接口中的屬性 x 和 y 都是只讀的,然后創(chuàng)建了一個 point 對象,類型為 Point2D,此時,我們不能再給對象中的 x 和 y 重新賦值,會報錯,因為它們都是只讀屬性。
此外 TypeScript 還提供了 ReadonlyArray 類型,它與 Array 相似,只是把所有可變方法去掉了,因此可以確保數(shù)組創(chuàng)建后再也不能被修改。
接口用于定義對象的結(jié)構(gòu),當(dāng)我們使用對象字面量賦值給接口類型時,TypeScript 會自動進行額外的屬性檢查。這意味著賦值的對象不能包含接口中未定義的額外屬性,否則會導(dǎo)致編譯錯誤。
在上述例子中,rect2 對象包含了額外的 color 屬性,但是接口 Rectangle 中并未定義該屬性,所以會導(dǎo)致編譯錯誤。
注意:如果我們確定對象會包含額外的屬性,可以使用類型斷言(Type Assertion)來繞過額外屬性檢查。
8. 接口的任意屬性有時候我們希望一個接口中除了包含必選和可選屬性之外,還允許有其他的任意屬性,這時我們可以使用 索引簽名 的形式來滿足上述要求。
上述代碼中,我們使用 [propName: string] 定義了任意屬性取 string 類型的值。
注意:一旦定義了任意屬性,那么必選屬性和可選屬性的類型都必須是它的類型的子集:
上述例子中,任意屬性的值允許是 string,但是可選屬性 age 的值卻是 number,number 不是 string 的子屬性,所以報錯了。
注意:一個接口中只能定義一個任意屬性。如果接口中有多個類型的屬性,則可以在任意屬性中使用聯(lián)合類型。
接口可以描述函數(shù)類型。
為了使用接口表示函數(shù)類型,我們需要給接口定義一個調(diào)用簽名。 它就像是一個只有參數(shù)列表和返回值類型的函數(shù)定義,參數(shù)列表里的每個參數(shù)都需要名字和類型。
在上述例子中,SearchFunc 是一個接口,它表示一個接收兩個參數(shù) source 和 subString,參數(shù)類型都為 string,并且返回值為 number 類型的函數(shù)。
這樣定義后,我們可以像使用其它接口一樣使用這個函數(shù)類型的接口。
下面的例子展示了如何創(chuàng)建一個函數(shù)類型的變量,并將一個同類型的函數(shù)賦值給這個變量。
注意:對于函數(shù)類型的類型檢查來說,函數(shù)的參數(shù)名不需要與接口里定義的名字相匹配。
例如,我們使用下面的代碼重寫上面的例子:
函數(shù)的參數(shù)會逐個進行檢查,要求對應(yīng)位置上的參數(shù)類型是兼容的。
如果你不想指定類型,TypeScript 的類型系統(tǒng)會推斷出參數(shù)類型,因為函數(shù)直接賦值給了 SearchFunc 類型變量。 函數(shù)的返回值類型是通過其返回值推斷出來的(此例是 false和true)。
如果讓這個函數(shù)返回數(shù)字或字符串,類型檢查器會警告我們函數(shù)的返回值類型與 SearchFunc 接口中的定義不匹配。
接口可以描述具有索引簽名的對象,這樣我們就可以通過索引來訪問對象的屬性。
上述的例子中,我們定義了 StringArray 接口,它具有索引簽名。這個索引簽名表示了當(dāng)用 number 去索引StringArray 時會得到 string 類型的返回值。
TypeScript 支持兩種索引簽名:字符串和數(shù)字。可以同時使用兩種類型的索引,但是數(shù)字索引的返回值必須是字符串索引返回值類型的子類型。 這是因為當(dāng)使用 number 來索引時,JavaScript 會將它轉(zhuǎn)換成 string 然后再去索引對象。 也就是說用 100(一個number)去索引等同于使用&34;100&34;(一個string)去索引,因此兩者需要保持一致。
11. 類類型實現(xiàn)接口接口可以被類實現(xiàn),稱為類類型。
類可以通過 implements 關(guān)鍵字來實現(xiàn)接口,并必須實現(xiàn)接口中定義的所有屬性和方法。
在上述例子中,Document 類實現(xiàn)了 Printable 接口,并實現(xiàn)了接口中定義的 print 方法。
12. 繼承接口和類一樣,接口也可以相互繼承。 這讓我們能夠從一個接口里復(fù)制成員到另一個接口里,可以更靈活地將接口分割到可重用的模塊里。
一個接口可以繼承多個接口,創(chuàng)建出多個接口的合成接口。
當(dāng)接口繼承了一個類類型時,它會繼承類的成員但不包括其實現(xiàn)。 就好像接口聲明了所有類中存在的成員,但并沒有提供具體實現(xiàn)一樣。 接口同樣會繼承到類的 private 和 protected 成員。 這意味著當(dāng)你創(chuàng)建了一個接口繼承了一個擁有私有或受保護的成員的類時,這個接口類型只能被這個類或其子類所實現(xiàn)(implement)。
在以上示例中,我們定義了一個 Animal 類,它有一個 name 屬性和一個 eat 方法。然后,我們定義了一個接口 CanRun,它繼承自 Animal 類,并添加了一個 run 和 eat 方法。接著,我們創(chuàng)建了一個 Dog 類來實現(xiàn) CanRun 接口,并在 Dog 類中實現(xiàn)了 run 和 eat 方法。
在最后的代碼中,我們使用 CanRun 接口來聲明一個 dog 對象,并將其實例化為 Dog 類的對象。這樣,我們可以通過調(diào)用 dog 對象的 eat 和 run 方法來驗證接口繼承類的實現(xiàn)。
接口繼承類的主要作用在于類型標(biāo)注和約束。 通過接口繼承類,我們可以定義更具體的接口類型,使得類和接口之間的關(guān)系更加清晰。同時,在使用接口類型的變量或參數(shù)時,可以享受到類成員的類型檢查和智能提示的功能。這對于代碼的可讀性、可維護性和可擴展性都有很大的幫助。
六、類型別名作用:
在 TS 中,類型別名主要用于為已有的類型創(chuàng)建別名,以便在代碼中更方便地引用和重用這些類型。
用法:
- 使用 type 關(guān)鍵字可以為任何類型定義別名,包括基本類型、復(fù)雜類型、函數(shù)類型等。
- 創(chuàng)建類型別名后,直接使用該類型別名作為變量的類型注解即可。
解釋:
- 類型別名是為已有類型提供另一個名稱,而不是創(chuàng)建新的類型。
- 類型別名可以用于簡化復(fù)雜類型的表達,提高可讀性和可維護性。
- 類型別名可以用于定義聯(lián)合類型或交叉類型的別名。
注意:
- 盡量選擇有意義的別名,能夠準(zhǔn)確描述類型的用途,提高代碼的可讀性。
- 避免過度使用類型別名,過多的別名可能導(dǎo)致代碼的可維護性變差。
- 注意避免循環(huán)引用的情況,即在類型別名中引用自身,這會導(dǎo)致編譯錯誤。
- 類型別名并不創(chuàng)建新的類型,所以它無法被繼承或?qū)崿F(xiàn)。
在 TypeScript 中,類型推論(Type Inference)是指編譯器在沒有明確指定類型的情況下,根據(jù)變量的值推斷出該變量的類型。 通過類型推論,TypeScript 可以在代碼中自動推斷出變量的類型,而無需顯式地將其指定為特定類型。
2. 基本類型推論當(dāng)聲明一個變量時,如果沒有顯式指定類型,并且在聲明的同時進行了賦值操作,TypeScript 將根據(jù)賦值的值推斷出變量的類型。
當(dāng)變量的類型與其所處的上下文相關(guān)時,TypeScript 可以根據(jù)上下文進行類型推斷。
在上述示例中,函數(shù) add 接收兩個參數(shù),并返回它們的和。當(dāng)我們調(diào)用 add(5, 10) 時,TypeScript 根據(jù)函數(shù)返回值的類型推斷出 result 變量的類型為 number。
4. 最佳通用類型推論當(dāng)需要推斷出數(shù)組或?qū)ο箢愋蜁r,TypeScript 會根據(jù)元素或?qū)傩缘念愋屯茢喑鲆粋€“最佳通用類型”。
在上述示例中,數(shù)組 numbers 中的所有元素都是數(shù)字,因此 TypeScript 推斷出 numbers 的類型為 number[]。而數(shù)組 mixed 中的元素類型不同(數(shù)字、字符串和布爾值),所以 TypeScript 推斷出 mixed 的類型為 (number | string | boolean)[],表示該數(shù)組可以存儲數(shù)字、字符串或布爾值類型的元素。
5. 聲明變量但沒有賦值的情況如果聲明變量的時候沒有賦值,不管之后有沒有賦值,都會被推斷成 any 類型而完全不被類型檢查。
在上述示例中,變量 str 的類型推斷為 any 類型,因為它沒有明確的初始值。此時我們就可以把任意類型的值賦值給 str。
需要注意的是,雖然 TypeScript 可以根據(jù)賦值來推斷類型,但如果變量的初始值為 null 或 undefined,類型推論仍然會將其推斷為 any 類型。
為了避免使用 any 類型,我們可以顯式指定變量的類型或為變量提供一個初始值來觸發(fā)類型推論。
八、類型斷言1. 定義類型斷言(Type Assertion)是 TypeScript 中的一種表達式,它可以用來告訴編譯器一個值的確切類型。通過類型斷言,我們可以在一些情況下主動指定變量的類型,以滿足特定的需求。
2. 語法類型斷言有2種語法形式:
- 尖括號語法: 使用尖括號 <> 將值包裹,并在尖括號內(nèi)指定目標(biāo)類型。 <類型>值
在上面的示例中,我們將變量 value 的類型斷言為 string 類型,然后使用 .length 屬性獲取字符串的長度。
- as 語法: 使用 as 關(guān)鍵字,在值后面跟上目標(biāo)類型。值 as 類型
在上面的示例中,我們使用 as 關(guān)鍵字將變量 value 的類型斷言為 string 類型,并用 length 屬性獲取字符串的長度。
以上兩種語法雖說沒有太大的區(qū)別,但是我們更推薦使用 as 語法。因為尖括號格式會與 react 中 JSX 產(chǎn)生語法沖突。
3. 任何類型可以斷言為 any 類型由于 any 類型可以接收任何值,因此任何類型都可以斷言為 any 類型。這樣的斷言并不提供更多的類型檢查,因此在使用類型斷言時需要謹慎。
上面的例子中,數(shù)字類型的變量 foo 上是沒有 length 屬性的,故 TypeScript 給出了相應(yīng)的錯誤提示。
這種錯誤提示顯然是非常有用的。
但有的時候,我們非常確定這段代碼不會出錯,比如下面這個例子:
上面的示例中,我們需要將 window 上添加一個屬性 bar,但 TypeScript 編譯時會報錯,提示我們 window 上不存在 屬性 bar。
此時我們可以使用 as any 臨時將 window 斷言為 any 類型:
與上述情況相反,由于 any 類型可以接收任何值,它可以被斷言為任何類型。這樣的斷言會跳過類型檢查,因此潛在的類型錯誤可能發(fā)生。
當(dāng)變量具有聯(lián)合類型時,我們可以通過類型斷言將其斷言為其中的一個類型,但是必須確保斷言的類型是變量實際上可以具備的類型。
類型斷言只是告訴編譯器將一個值視為特定類型,并不會改變該值的實際類型。在運行時,類型斷言不會影響變量的值或行為,它只是在編譯時起作用。
6.2. 類型斷言不能用于基本類型之間的轉(zhuǎn)換TypeScript 的類型斷言不能用于將基本類型(如 number、string、boolean)相互轉(zhuǎn)換。因為基本類型具有明確的類型判斷和行為,不能將一個基本類型斷言為另一個基本類型。
類型斷言可以繞過編譯器的類型檢查,但并不意味著我們可以隨意斷言任何類型。如果發(fā)生類型斷言與變量的實際類型不匹配的情況,可能會導(dǎo)致運行時錯誤。
因為 null 和 undefined 可以被賦值給任何類型,將它們斷言為其他類型是沒有意義的。
如果將一個變量斷言為聯(lián)合類型中某個類型,那么它必須是該聯(lián)合類型中的實際類型之一。
雙重斷言(Double Assertion),也被稱為雙重類型斷言或連續(xù)類型斷言,是一種在 TypeScript 中連續(xù)使用類型斷言的技術(shù)。它是將一個值斷言為多個類型的一種嘗試,盡管這種用法并不被 TypeScript 官方鼓勵使用,因為它可能產(chǎn)生不可預(yù)測的結(jié)果。
雙重斷言的形式是使用連續(xù)的類型斷言操作符 as 或尖括號 <> 來表示:
在上述示例中,我們連續(xù)使用了兩次類型斷言,將值 value 先斷言為 any 類型,然后再將其斷言為 string 類型,并使用 length 屬性獲取字符串的長度。但是需要注意的是,盡管代碼通過了編譯,但是這種雙重斷言的方法并不安全,因為它可以導(dǎo)致類型錯誤和運行時錯誤。
使用雙重斷言可能會隱藏類型錯誤,因為類型斷言是編譯時的操作,而不是運行時。在運行時,雙重斷言可能會導(dǎo)致意外的類型轉(zhuǎn)換錯誤,并且編譯器無法為此提供任何保護。
所以,在實際開發(fā)中,應(yīng)盡量避免使用雙重斷言。如果需要使用多個類型,而無法使用更安全的方法來表示,可以考慮重構(gòu)代碼,使用更合適的類型來處理多種情況,或者使用類型守衛(wèi)和類型判斷等 TypeScript 提供的更安全的技術(shù)來處理復(fù)雜的類型轉(zhuǎn)換或條件判斷。
8. 類型斷言VS類型轉(zhuǎn)換在 TypeScript 中,類型斷言(Type Assertion) 是一種在編譯時告訴編譯器一個值的確切類型的方式,它只是一種類型的聲明,不會對變量進行真正的類型轉(zhuǎn)換。
與類型斷言相對的是類型轉(zhuǎn)換(Type Casting) ,它是將一個值從一種類型轉(zhuǎn)換為另一種類型的實際操作,而不僅僅是告訴編譯器某個值的類型。類型轉(zhuǎn)換通常需要在運行時進行,并涉及對值的實際修改。
在上述示例中,(value as string) 是一種類型斷言,告訴編譯器將變量 value 視為字符串類型。而 parseInt 是一種類型轉(zhuǎn)換,將字符串類型的 numberValue 轉(zhuǎn)換為整數(shù)類型。
需要注意的是,類型斷言只會在編譯時起作用,不會對變量進行實際的類型轉(zhuǎn)換。而類型轉(zhuǎn)換涉及到對變量值的修改,通常發(fā)生在運行時。
盡管類型斷言和類型轉(zhuǎn)換在某種程度上可以實現(xiàn)相似的效果,但它們的機制和目的不同。類型斷言是為了輔助編譯器進行類型推斷和類型檢查的工具,而類型轉(zhuǎn)換是為了實際修改變量的類型以滿足特定需求。因此,在使用類型轉(zhuǎn)換時,需要注意潛在的類型錯誤和運行時錯誤,并謹慎處理類型轉(zhuǎn)換的結(jié)果。
9. 類型斷言VS類型聲明在 TypeScript 中,類型斷言(Type Assertion) 是一種在編譯時告訴編譯器一個值的確切類型的方式,它是開發(fā)者主動指定一個變量的類型,并告訴編譯器遵循這個類型進行類型檢查。通過類型斷言,我們可以在某些情況下繞過編譯器的類型檢查,但這需要開發(fā)者對類型的準(zhǔn)確性負責(zé),并且存在潛在的類型錯誤的風(fēng)險。
在上述示例中,(value as string) 是一種類型斷言,將變量 value 的類型斷言為字符串類型,從而可以安全地訪問字符串的 length 屬性。
類型聲明(Type Declaration) 是一種為變量、參數(shù)、返回值等明確指定類型的語法,它是用來定義變量的類型,并告訴編譯器如何對變量進行類型推斷和類型檢查。類型聲明通常出現(xiàn)在變量聲明、函數(shù)聲明、函數(shù)參數(shù)、函數(shù)返回值等地方,例如:
在上述示例中,value: string 是對變量 value 進行類型聲明,指定其類型為字符串。而 name: string 是對函數(shù)參數(shù) name 進行類型聲明,指定其類型為字符串。這樣可以確保編譯器在類型檢查時能夠發(fā)現(xiàn)潛在的類型錯誤。
類型聲明是 TypeScript 中一種重要的類型系統(tǒng)的特性,它提供了對變量類型的明確說明,使開發(fā)者能夠編寫更加安全和可維護的代碼。與類型斷言相比,類型聲明更加強制,能夠更好地幫助開發(fā)者在編譯時發(fā)現(xiàn)類型錯誤,并提供更好的類型推斷和類型檢查支持。
10. 類型斷言和泛型在 TypeScript 中,類型斷言(Type Assertion) 是一種在編譯時告訴編譯器一個值的確切類型的方式,它是開發(fā)者主動指定一個變量的類型,并告訴編譯器遵循這個類型進行類型檢查。通過類型斷言,我們可以在某些情況下繞過編譯器的類型檢查,但這需要開發(fā)者對類型的準(zhǔn)確性負責(zé),并且存在潛在的類型錯誤的風(fēng)險。
在上述示例中,(value as string) 是一種類型斷言,將變量 value 的類型斷言為字符串類型,以便可以安全地訪問字符串的 length 屬性。
泛型是一種在定義函數(shù)、類或接口時使用類型參數(shù)來表示靈活的類型的方式。通過泛型,我們可以在定義時不指定具體類型,而是在使用時根據(jù)上下文傳入具體的類型。它可以增加代碼的重用性和靈活性。例如:
在上述示例中,toArray 是一個泛型函數(shù),使用類型參數(shù) T 來表示數(shù)組中的元素類型。通過傳入具體的類型 &34;Hello&34;,我們可以創(chuàng)建一個字符串類型的數(shù)組。
類型斷言和泛型實際上可以一起使用。當(dāng)我們在處理泛型類型時,有時可能需要對類型進行斷言以滿足特定的需求。例如:
在上述示例中,通過連續(xù)使用類型斷言,我們將泛型類型 T 先斷言為 unknown 類型,然后再斷言為字符串類型,將參數(shù) value 轉(zhuǎn)換為字符串類型并返回。
需要注意的是,在使用類型斷言和泛型時,我們要確保類型的安全性和正確性,并避免潛在的類型錯誤。類型斷言可以幫助我們處理一些特殊情況,但要謹慎使用,并確保斷言的類型與變量的實際類型相符。泛型則是一種更加靈活和通用的方式來處理不特定類型的代碼邏輯。
九、類(class)1. 類的定義在 TypeScript 中,可以使用 class 關(guān)鍵字來定義類。類的定義通常包括成員變量、構(gòu)造函數(shù)、方法等。
2. 類的基本使用類的基本使用主要有以下幾個步驟:
- 定義類及成員變量: 使用 class 關(guān)鍵字定義一個類,并在類中聲明成員變量。
- 構(gòu)造函數(shù): 使用 constructor 方法定義構(gòu)造函數(shù),用于在創(chuàng)建類的實例時初始化對象的屬性。
- 方法: 在類中定義方法,可通過類的實例調(diào)用。
- 創(chuàng)建類的實例: 使用 new 關(guān)鍵字創(chuàng)建類的實例,并傳遞構(gòu)造函數(shù)所需的參數(shù)。
- 訪問成員變量和調(diào)用方法: 通過實例對象訪問成員變量和調(diào)用方法。
在上述示例中:我們使用 class 關(guān)鍵字定義一個名為 Person 的類,并在 Person 類中聲明了兩個成員變量:name 和 age。接著,我們使用 constructor 方法定義一個構(gòu)造函數(shù),用于在創(chuàng)建類的實例時初始化對象的屬性,構(gòu)造函數(shù)參數(shù) name 和 age 分別用于接收傳入的 name 和 age 值,并將其賦給對應(yīng)的成員變量。然后定義了一個名為 sayHello 的方法,用于打印一個問候語,并使用成員變量 name 和 age。接著,我們使用 new 關(guān)鍵字創(chuàng)建一個 Person 實例 p,然后打印出 name 和 age 的值以及調(diào)用 sayHello 方法。
3. 類的構(gòu)造函數(shù)在 TypeScript 類中,構(gòu)造函數(shù)是一種特殊的方法,用于在創(chuàng)建類的實例時進行初始化操作。構(gòu)造函數(shù)使用 constructor 關(guān)鍵字來定義,可以接收參數(shù),并在創(chuàng)建對象時調(diào)用。
3.1. 構(gòu)造函數(shù)的基本語法在上面的代碼中,ClassName 是類的名稱,parameter1、parameter2 等表示構(gòu)造函數(shù)的參數(shù)名,Type1、Type2 等表示參數(shù)的類型。
3.2. 使用構(gòu)造函數(shù)初始化成員變量構(gòu)造函數(shù)可以用來初始化類中的成員變量,通過接收構(gòu)造函數(shù)的參數(shù),并將其賦給對應(yīng)的成員變量。成員變量的聲明通常放在類的頂部,而初始化則在構(gòu)造函數(shù)中進行。
在上述示例中,構(gòu)造函數(shù)接收 name 和 age 作為參數(shù),并將參數(shù)的值分別賦給類中的 name 和 age 成員變量。
3.3. 創(chuàng)建類的實例并調(diào)用構(gòu)造函數(shù)使用 new 關(guān)鍵字創(chuàng)建類的實例時,構(gòu)造函數(shù)會被自動調(diào)用,讓我們可以在創(chuàng)建實例的同時進行初始化操作。
在上述代碼中,我們創(chuàng)建了一個 Person 類的實例 person,并傳遞了 &39;Echo&39; 和 26 作為構(gòu)造函數(shù)的參數(shù)。構(gòu)造函數(shù)會將這些參數(shù)的值分別賦給 person 實例的 name 和age 成員變量。
3.4. 構(gòu)造函數(shù)的可選參數(shù)和默認值構(gòu)造函數(shù)的參數(shù)可以設(shè)置為可選的,并且可以為參數(shù)提供默認值。
可選參數(shù)使用問號( ? )修飾符進行標(biāo)記,而默認值則使用等號(=)進行賦值。
在上述示例中,name 參數(shù)具有一個默認值 &39;Echo&39;,而 age 參數(shù)則是可選的。如果在創(chuàng)建實例時不傳 name 和 age 參數(shù),那么 name 會輸出默認值 &39;Echo&39;,而 age 會被設(shè)置為 undefined,如果在創(chuàng)建實例時只傳遞了 name 參數(shù),而沒有傳遞 age 參數(shù),那么 age 也會被設(shè)置為 undefined。
3.5 .調(diào)用其他構(gòu)造函數(shù)(構(gòu)造函數(shù)重載)在一個類中,可以定義多個構(gòu)造函數(shù),并通過不同的參數(shù)配置來進行重載。重載的構(gòu)造函數(shù)之間可以相互調(diào)用,使用 this 關(guān)鍵字來引用當(dāng)前類的實例。
構(gòu)造函數(shù)重載需要定義多個具有不同參數(shù)類型和數(shù)量的構(gòu)造函數(shù)簽名。構(gòu)造函數(shù)簽名是指構(gòu)造函數(shù)名稱和參數(shù)列表,通過這些不同的簽名來區(qū)分不同的構(gòu)造函數(shù)。
在上面的示例中,我們定義了三個構(gòu)造函數(shù)簽名,每個簽名有不同的參數(shù)類型和數(shù)量,以提供不同的構(gòu)造函數(shù)選項。
在上述示例中,我們定義了兩個構(gòu)造函數(shù)簽名,第一個構(gòu)造函數(shù)接收一個 name 參數(shù),第二個構(gòu)造函數(shù)接收一個 name 和一個 age 參數(shù)。在構(gòu)造函數(shù)的實現(xiàn)中,根據(jù)傳遞的參數(shù)情況,決定是否給 age 成員變量賦值。接著,我們創(chuàng)建了兩個實例 person1 和 person2,第一次實例化傳遞了一個 name 參數(shù),調(diào)用了第一個構(gòu)造函數(shù)。第二次實例化傳遞了一個 name 參數(shù)和一個 age 參數(shù),調(diào)用了第二個構(gòu)造函數(shù)。
注意:
- 成員初始化(比如 name: string)后,才可以通過 this.name 來訪問實例成員。
- 需要為構(gòu)造函數(shù)指定類型注解,否則會被隱式推斷為 any 類型,構(gòu)造函數(shù)不需要返回值類型。
在 TypeScript 類中,實例方法是定義在類中的成員方法,用于操作和訪問類的實例屬性,并執(zhí)行特定的操作。實例方法可以通過類的實例來調(diào)用,用于對特定實例進行特定操作。
4.1. 定義實例方法實例方法是通過在類中定義普通函數(shù)來創(chuàng)建的。語法格式如下:
在上面的示例中,methodName 是實例方法的名稱,parameter1 和 parameter2 是方法的參數(shù),Type1 和 Type2 是參數(shù)的類型,ReturnType 是方法的返回類型。
4.2. 訪問實例屬性實例方法可以通過使用 this 關(guān)鍵字直接訪問類的實例屬性。
在上述示例中,sayHello 是一個實例方法,它訪問了 Person 類的 name 和 age 屬性,并在控制臺打印出相應(yīng)的消息。
4.3. 調(diào)用實例方法實例方法必須通過類的實例來調(diào)用。
在上述示例中,我們首先創(chuàng)建了一個 Person 類的實例 person,然后使用 person 實例來調(diào)用 sayHello 方法。
5. 類的繼承類的繼承有2種方式:
- extends(繼承父類)
- implements(實現(xiàn)接口)
說明:JS 中只有 extends,而 implements 是 TS 提供的。
5.1. extends(繼承父類)當(dāng)一個類繼承另一個類時,它會繼承父類的屬性和方法,并可以通過重載或添加新的屬性和方法來擴展父類。繼承使用 extends 關(guān)鍵字來建立類之間的關(guān)系。
5.1.1. 定義父類和子類父類是被繼承的類,子類是繼承父類的類。
在上面的示例中,ParentClass 是父類,ChildClass 是子類,ChildClass 繼承了 ParentClass 的屬性和方法。
5.1.2. 繼承父類的屬性和方法使用 extends 關(guān)鍵字來建立子類對父類的繼承關(guān)系。子類會繼承父類的公共成員(屬性和方法)。子類可以直接訪問和使用繼承來的屬性和方法。
在上述示例中,Animal 是父類,其中包含了 name 屬性和 move 方法。Dog 是子類,使用 extends Animal 建立了繼承關(guān)系。Dog 繼承了 Animal 的屬性和方法,并且定義了自己的 bark 方法。
5.1.3. 調(diào)用繼承的屬性和方法子類可以直接調(diào)用繼承來的父類屬性和方法,也可以訪問自己定義的屬性和方法。
在上述示例中,我們首先創(chuàng)建了一個 Dog 類的實例 dog。我們可以通過 dog 實例調(diào)用繼承自父類的 move 方法,也可以調(diào)用子類自己定義的 bark 方法。
5.2. implements(實現(xiàn)接口)接口的實現(xiàn)是以類為基礎(chǔ)的,類可以通過 implements 關(guān)鍵字實現(xiàn)一個或多個接口。通過實現(xiàn)接口,類必須提供接口中定義的所有屬性和方法的具體實現(xiàn)。
5.2.1. 定義接口接口是一種抽象的類型,定義了一組屬性和方法的規(guī)范。接口在定義時不包含具體的實現(xiàn),而是描述了類應(yīng)具備的特定行為和功能。
在上面的示例中,InterfaceName 是一個接口,用于定義屬性和方法的規(guī)范。
5.2.2. 使用 implements 實現(xiàn)接口使用 implements 關(guān)鍵字來實現(xiàn)接口,使得類能夠滿足接口定義的規(guī)范。通過實現(xiàn)接口,類必須提供接口中定義的所有屬性和方法的具體實現(xiàn)。
在上述示例中,ClassName 是一個類,通過 implements InterfaceName 實現(xiàn)了接口 InterfaceName,從而滿足了接口定義的規(guī)范。
5.2.3. 實現(xiàn)接口的屬性和方法實現(xiàn)接口的類必須包含接口中定義的所有屬性和方法,并提供它們的具體實現(xiàn)。
在上面的示例中,Shape 是一個接口,定義了屬性 color 和方法 getArea()。Circle 類通過 implements Shape 實現(xiàn)了接口 Shape,并提供了接口中定義的屬性和方法的具體實現(xiàn)。
6. 類的修飾符在 TypeScript 中,類的修飾符用于控制類的成員(屬性和方法)的可見性和訪問權(quán)限。
類的修飾符包括:
- public(公有的),可以在任何地方被訪問到,默認所有的屬性和方法都是 public 的。
- privete(私有的),不能在聲明它的類的外部訪問。
- protected(受保護的),和 private 類似,區(qū)別是它在子類中也是允許被訪問的。
public 關(guān)鍵字是默認的訪問修飾符,如果不指定修飾符,默認為 public。公共成員在類的內(nèi)部和外部都是可見的,并且可以隨時訪問。
在上述示例中,name、age 和 sayHello() 都是公共成員,可以在類的內(nèi)部和外部進行訪問。
6.2. privateprivate 關(guān)鍵字修飾符限制成員的訪問范圍僅在類的內(nèi)部。私有成員在類的外部不可見,只能在類的內(nèi)部進行訪問。
在上述示例中,成員 name 是私有成員,只能在類的內(nèi)部進行訪問,外部訪問會報錯。
注意:1. 使用 private 修飾的屬性或方法,在子類中也是不允許訪問的。
注意:2. 當(dāng)構(gòu)造函數(shù)修飾為 private 時,該類不允許被繼承或者實例化。
protected 關(guān)鍵字修飾符限制成員的訪問范圍在類的內(nèi)部及其派生類中。受保護成員在類的外部不可見,但可以在類的內(nèi)部和派生類中進行訪問。
注意:當(dāng)構(gòu)造函數(shù)修飾為 protected 時,該類只允許被繼承。
readonly 是一個只讀屬性關(guān)鍵字,只允許出現(xiàn)在屬性聲明或索引簽名或構(gòu)造函數(shù)中。
注意:如果 readonly 和其他訪問修飾符同時存在的話,需要寫在其后面。
readonly 只讀屬性特點:
- 只讀屬性必須在聲明時或索引簽名或構(gòu)造函數(shù)內(nèi)進行初始化賦值。
- 只讀屬性不能被重新賦值或修改,否則會報錯。
- 只能修飾屬性,不能修飾方法。
只讀屬性和常量的區(qū)別:
- 只讀屬性是 TypeScript 提供的一種語法,用于將類的屬性標(biāo)記為只讀,并且只有在類的內(nèi)部可以修改其值。
- 常量通常是通過 const 關(guān)鍵字聲明的,在任何地方都無法修改其值,包括類的內(nèi)部。
參數(shù)屬性是一種簡化代碼的語法糖,用于在構(gòu)造函數(shù)中同時聲明和初始化類的成員屬性。使用參數(shù)屬性可以在一個地方完成屬性的聲明和賦值,減少了重復(fù)的代碼。
在上述示例中,定義了一個名為 Person 的類,類里面定義了一個 constructor 構(gòu)造方法,其中參數(shù) name 是公共屬性,可以在類的內(nèi)部和外部訪問;參數(shù) age 是私有屬性,只能在類 Person 中訪問;參數(shù) sex 是受保護屬性,只能在類 Person 及其子類中訪問;參數(shù) height 是只讀屬性,類的外部無法修改其值。
7. 抽象類使用關(guān)鍵字 abstract 用于定義抽象類和其中的抽象方法。
抽象類是一種不能直接實例化的類,它主要用作其他類的基類。抽象類可以包含抽象方法和具體方法的定義,供子類繼承和實現(xiàn)。
7.1. 語法在上述示例中,AbstractClass 是一個抽象類,它包含了一個抽象方法 method() 和一個具體方法 concreteMethod()。
7.2. 抽象方法抽象方法是在抽象類中聲明但沒有具體實現(xiàn)的方法。它只包含方法的簽名,沒有方法體,子類必須實現(xiàn)抽象方法。
在上述示例中,抽象類 Animal 中的 sayHi() 是一個抽象方法,子類 Cat 繼承了 父類 Animal 并實現(xiàn)了抽象方法。
7.3. 抽象類不能被實例化,只能被繼承抽象類不能被實例化,只能被繼承。
- 抽象類不能被實例化,只能被繼承。
- 抽象類可以包含抽象方法和具體方法的定義。
- 子類必須實現(xiàn)抽象類中的所有抽象方法,否則子類也必須聲明為抽象類。
- 如果一個類繼承了一個抽象類,那么它必須實現(xiàn)抽象類中的抽象方法,除非它自身也聲明為抽象類。
- 抽象類可以作為其他類的基類,用于提供共享的屬性和方法定義。
類型兼容性是指在 TS 中,如何判斷一個類型是否能夠賦值給另一個類型。
1. 基本類型的兼容性1.1. 相同的基本類型可以互相賦值當(dāng)你聲明一個變量并為其賦予一個特定類型的值時,TypeScript 會根據(jù)類型注解進行類型檢查和推斷。如果變量的類型與給定的值的類型完全匹配,那么它們可以互相賦值。
在上述示例中,變量 a 被聲明為 number 類型,并且被賦值為 10。 然后將變量 a 賦值給變量 b,因為 a 和 b 的類型相同,都是 number,所以賦值是允許的。
1.2. 數(shù)字字面量類型可以賦值給數(shù)值類型當(dāng)你聲明一個變量并為其指定為數(shù)字字面量類型時,TypeScript 會將該變量視為一個特定的數(shù)字值,而不僅僅是一般的數(shù)值類型。
在這個示例中,變量 a 被聲明為數(shù)字字面量類型 10,它只能具有值 10,而不能是其它的值。然后將變量 a 賦值給變量 b,因為 b 的類型是 number,而 a 是數(shù)字字面量類型 5,數(shù)字字面量類型是數(shù)字類型的子類型,所以賦值是允許的。
需要注意的是,只有字面量類型才可以賦值給相應(yīng)的數(shù)值類型,普通數(shù)值類型不能賦值給字面量類型,除非兩者完全匹配。
1.3. 枚舉類型可以賦值給數(shù)字類型枚舉類型在 TypeScript 中被編譯成了一個具有反向映射的對象。默認情況下,枚舉類型的成員值是從 0 開始遞增的數(shù)字。由于枚舉成員值是數(shù)字類型,所以它們可以被賦值給數(shù)字類型。
在上述示例中,將 Direction.Right 賦值給了枚舉類型的變量 direction,然后又將 direction 賦值給了數(shù)字類型的變量 num,此時 num 的值為 1,與 Direction.Right 對應(yīng)的枚舉成員值相同。
需要注意的是,枚舉類型不僅可以賦值給數(shù)字類型,也可以賦值給字面量類型或其他兼容的類型。這主要是由于 TypeScript 在類型系統(tǒng)中對枚舉類型進行了特殊處理,使得枚舉成員值可以被當(dāng)作相應(yīng)的字面量值使用。
2. 對象類型的兼容性對象類型包括接口(interface)、類(class)、字面量對象等。
記住這句話:成員多的可以賦值給成員少的。
2.1. 成員個數(shù)的兼容性對象類型 T 能夠賦值給對象類型 U,需要滿足的條件是 T 中的成員個數(shù)要大于等于 U 中的成員個數(shù)。也就是說,T 可以擁有 U 中的所有成員,但 T 可能還有額外的成員。
在上述示例中,類 Point2D 具有 x 和 y 成員,類 Point3D 比類 Point2D 多了一個 z 成員,根據(jù)兼容性規(guī)則,Point3D 可以賦值給 Point2D,因為類 Point3D 擁有類 Point2D 中的所有成員。
2.2. 成員類型的兼容性對象類型 T 能夠賦值給對象類型 U,需要滿足的條件是 T 中的每個成員的類型都能夠賦值給 U 中對應(yīng)成員的類型。這個規(guī)則適用于成員變量和成員函數(shù)。
對象類型 T 能夠賦值給對象類型 U,如果 U 中定義了可選屬性,且 T 中沒有對應(yīng)的屬性,則仍然可以進行賦值。
函數(shù)之間的兼容性會比較復(fù)雜,需要考慮以下幾個方面:
- 參數(shù)個數(shù)
- 參數(shù)類型
- 返回值類型
源函數(shù)的參數(shù)個數(shù)要小于等于目標(biāo)函數(shù)的參數(shù)個數(shù)。也就是說,源函數(shù)可以接受更少的參數(shù)或與目標(biāo)函數(shù)相同數(shù)量的參數(shù)。多余的參數(shù)是允許的,因為在函數(shù)調(diào)用時可以忽略它們。
記住這句話:參數(shù)少的可以賦值給參數(shù)多的。
在上述示例中,我們定義了兩個類型 Adder 和 Calculator 分別表示加法函數(shù)和計算函數(shù)。根據(jù)函數(shù)兼容性規(guī)則,add 可以賦值給 calculate,因為 Adder 的參數(shù)個數(shù)(2個)少于 Calculator 的參數(shù)個數(shù)(3個)。但是相反的賦值會導(dǎo)致兼容性錯誤,因為 Calculator 的參數(shù)個數(shù)(3個)要多于 Adder 的參數(shù)個數(shù)(2個)。
3.2. 參數(shù)類型在上述示例中,函數(shù) x 的參數(shù)只有一個 a,類型為 number,函數(shù) y 的參數(shù)有兩個 a 和 b,類型分別為 number 和 string,x 可以賦值給 y,是因為 x 的每個參數(shù)都能在 y 里找到對應(yīng)類型的參數(shù)。 注意的是參數(shù)的名字相同與否無所謂,只看它們的類型。 而 y 不能賦值給 x,因為 y 有個必需的第二個參數(shù),但是 x 并沒有,所以不允許賦值。
3.3. 返回值類型如果返回值類型是普通類型,此時函數(shù)的返回值類型要相同。
如果返回值類型是對象類型,此時成員多的可以賦值給成員少的。
類與對象字面量和接口差不多,但有一點不同:類有靜態(tài)部分和實例部分的類型。 比較兩個類類型的對象時,只有實例的成員會被比較。 靜態(tài)成員和構(gòu)造函數(shù)不在比較的范圍內(nèi)。
私有的和受保護的成員必須來自于相同的類或者父類的派生類。
當(dāng)泛型類型沒有明確指定類型參數(shù)時,它被認為是一種特殊的兼容性形式,稱為類型參數(shù)的默認,即泛型函數(shù)或泛型類在沒有傳遞類型參數(shù)的情況下,它們的類型參數(shù)會被推導(dǎo)為any。此時,泛型類型可以兼容任意類型,也能賦值給其他泛型類型。
當(dāng)泛型類型明確指定了類型參數(shù)時,要求類型參數(shù)具有兼容的類型。這意味著泛型類型在傳遞不同類型參數(shù)時,需要確保它們之間滿足兼容性規(guī)則。
交叉類型類似于接口繼承,是將多個類型合并為一個類型。 也就是說我們可以把現(xiàn)有的多種類型疊加到一起成為一種類型,它包含了所需的所有類型的特性。
使用符號( & )來定義交叉類型。
1. 組合對象類型在上述示例中,我們定義了 User 和 Admin 兩個類型,然后使用交叉類型 & 將 User & Admin 連接起來創(chuàng)建了一個新的類型 UserAdmin,該類型包含了 User 和 Admin 類型的所有成員,接著我們定義了一個變量 userAdmin,該變量同時具有 User 和 Admin 類型的屬性和方法。
2. 合并函數(shù)類型在上述示例中,我們定義了兩個函數(shù)類型 AddFunc 和 MultiplyFunc,AddFunc 里面定義了 fn 函數(shù),MultiplyFunc 里面定義了 fn1 函數(shù),并使用交叉類型 & 將 AddFunc & MultiplyFunc 連接起來創(chuàng)建了一個新的類型 MathOperations。此時變量 mathOps 同時擁有 fn 和 fn1 兩個方法。
3. 交叉類型VS接口繼承- 相同點:都可以實現(xiàn)對象類型的組合。
- 不同點:兩種方式實現(xiàn)類型組合時,對于同名屬性之間,處理類型沖突的方式不同。
下面是接口繼承的示例,接口B繼承接口A,兩個接口都定義了 fn 方法,返回值都是 string 類型,但是參數(shù)的類型不同,一個 string,一個 number,由于 fn 參數(shù) value 的類型不兼容,所以接口 B 不能繼承接口 A。
下面是交叉類型的示例:我們定義了 A 和 B 兩個接口,然后使用交叉類型 & 將 A & B 連接起來創(chuàng)建了一個新的類型 ,接著我們定義了一個變量 c,類型為 C,變量 c 調(diào)用 fn 方法,此時參數(shù)的類型我們可以傳數(shù)字類型或者字符串類型。
如果合并的多個接口類型存在同名屬性會是什么效果呢?
在上面示例中,定義了兩個類型 User 和 Admin,其中類型 User 中有 id 和 name 屬性,類型 Admin 中有 name 和 age 屬性,兩個類型都有同名的 name 屬性,但類型不同,一個是 string,一個是 number,合并后,name 屬性的類型就是 string 和 number 兩個原子類型的交叉類型,即 never。
此時,我們?nèi)绻x予 user 任意類型的 name 屬性值都會提示類型錯誤。而如果我們不設(shè)置 name 屬性,又會提示一個缺少必選的 name 屬性的錯誤。在這種情況下,就意味著上述代碼中交叉出來的 UserAdmin 類型是一個無用類型。
如果同名屬性的類型兼容,比如一個是 number,另一個是 number 的子類型、數(shù)字字面量類型,合并后 name 屬性的類型就是兩者中的子類型。
在上面示例中,name 屬性的類型就是數(shù)字字面量類型 2,因此,我們不能把任何非 2 之外的值賦予 name 屬性。
如果交叉類型中的某個成員是對象類型,那么交叉后的類型將擁有這些對象類型的所有屬性
泛型(Generics)是 TypeScript 中一種允許我們在定義函數(shù)、類或接口時使用參數(shù)化類型的機制。泛型可以看作是類型參數(shù),類似于函數(shù)中的參數(shù),但是用于表示類型而不是值。它允許我們在定義函數(shù)、類或接口時使用占位符表示類型,并在實際使用時指定具體的類型。
2. 一個簡單的例子現(xiàn)在我們有個需求:實現(xiàn)一個函數(shù),傳入的函數(shù)參數(shù)是什么類型的,返回值的類型也要跟函數(shù)參數(shù)的類型相同,并且函數(shù)只能接收一個參數(shù),你會怎么做?
上面的示例中,我們創(chuàng)建了一個 identity 函數(shù),參數(shù)值和返回值類型都為 number,調(diào)用 identity 函數(shù),傳入一個數(shù)字,會返回數(shù)字本身。但是,該函數(shù)只能接收數(shù)值類型,如果我調(diào)用函數(shù)的時候傳入字符串或者布爾值類型的值,此時就會報錯。
為了讓函數(shù)能夠接收任意類型,可以將參數(shù)類型改為any,但是,這樣就失去了 TS 的類型保護,類型不安全。
為了解決上面的這些問題,我們使用泛型對上面的代碼進行重構(gòu)。 泛型在保證類型安全(不丟失類型信息)的同時,可以讓函數(shù)等于多鐘不同的類型一起工作,靈活可復(fù)用。
上面示例中,我們在函數(shù)名 identity 后添加了 ,其中 T 代表 Type,在定義泛型時通常用作第一個類型變量名稱。但實際上 T 可以用任何有效名稱代替。在調(diào)用函數(shù) identity 時,在<>中指定類型 string,此時參數(shù)和返回值類型也都為 string。
3. 泛型語法- 在函數(shù)名稱的后面添加尖括號( <> ),尖括號中添加類型變量,比如下圖中的 T。
- 其中 T 代表 Type,可以是任意合法的變量名稱。
- 類型變量 T,是一種特殊類型的變量,它用于處理類型而不是值。
- 該類型變量相當(dāng)于一個類型容器,能夠捕獲用戶提供的類型(具體是什么類型,由用戶調(diào)用該函數(shù)時指定)。
- 因為 T 是類型,因此可以將其作為函數(shù)參數(shù)和返回值的類型,表示參數(shù)和返回值具有相同的類型。
在下面的示例中,調(diào)用泛型函數(shù) identity,當(dāng)傳入類型 number 后,這個類型就會被函數(shù)聲明時指定的類型變量 T 捕獲到,此時,T 的類型就是 number,所以,函數(shù) identity 的參數(shù)和返回值的類型也都是 number。
- 在調(diào)用泛型函數(shù)時,可以省略<類型>來簡化泛型函數(shù)的調(diào)用。
- 此時,TS 內(nèi)部會采用一種叫做類型參數(shù)推斷的機制,來根據(jù)傳入的實參自動推斷出類型變量 T 的類型。
- 當(dāng)編譯器無法推斷類型或者推斷的類型不準(zhǔn)確時,就需要顯示地傳入類型參數(shù)。
比如,傳入實參10,TS 會自動推斷出變量 num 的類型 number,并作為 T 的類型。
定義泛型的時候,可以一次定義多個類型參數(shù):
上述示例中,我們定義了一個 swap 函數(shù),用來交換輸入的元組。
6. 泛型類泛型類(Generic Class)是指在定義類時使用泛型類型參數(shù)的類。它允許我們在類的屬性、方法、構(gòu)造函數(shù)以及實例化時使用泛型。
- 在 class 名稱后面添加 <類型變量> ,這個類就變成了泛型類。
- 在創(chuàng)建 class 實例時,在類名后面通過 <類型> 來指定明確的類型。
下面是一個簡單的泛型類的示例:
- 在接口名稱的后面添加 <類型變量> ,那么,這個接口就變成了泛型接口。
- 接口的類型變量,對接口中所有其它成員可見,也就是接口中所有成員都可以使用類型變量。
- 使用泛型接口時,需要顯示指定具體的類型。
下面是一個簡單的泛型接口的示例:
在 TypeScript 2.3 以后,我們可以為泛型中的類型參數(shù)指定默認類型。當(dāng)使用泛型時沒有在代碼中直接指定類型參數(shù),從實際值參數(shù)中也無法推測出時,這個默認類型就會起作用。
默認情況下,泛型函數(shù)的類型參數(shù) T 理論上是可以是任何類型的,不同于 any,你不管使用它的什么屬性或者方法都會報錯(除非這個屬性和方法是所有集合共有的)。
比如下面的示例中,我想打印出參數(shù)的 length 屬性,如果不進行泛型約束 TS 是會報錯的:類型“T”上不存在屬性“l(fā)ength”。
報錯的原因很明顯,如果要解決這個問題,我們就可以通過給泛型(類型變量)添加約束。
下面我們通過 extends 關(guān)鍵字進行類型約束:
在上述示例中,我們定義了一個 ILength 接口,具有 length 屬性。在泛型函數(shù) getLength 中,使用 T extends ILength 進行約束,該約束表示:傳入的類型必須具有 length 屬性。
十三、TS中的關(guān)鍵字TS 內(nèi)置了一些常用的工具類型,來簡化 TS 中一些常見的操作,它們都是基于泛型實現(xiàn)的,并且是內(nèi)置的,所以可以直接使用。
在學(xué)習(xí)工具類型之前,我們先學(xué)習(xí)一些關(guān)鍵字和基礎(chǔ)知識,以便我們可以更好的去學(xué)習(xí)后面的內(nèi)置工具類型。
1. keyof在 TS 中,keyof 操作符主要用途是用于獲取類型中所有鍵的關(guān)鍵字。它用于泛型中,通常與索引類型(index type)結(jié)合使用。其返回類型是聯(lián)合類型。
下面示例中,我們定義了一個接口 Person,包含 name、age 和 gender 三個鍵,然后使用 keyof 來獲取 Person 接口的所有鍵,這樣,Keys 類型就是一個由 &34;name&34; | &34;age&34; | &34;gender&34; 構(gòu)成的聯(lián)合字面量類型。
下面示例中,我們創(chuàng)建一個函數(shù)來獲取對象中屬性的值:
在 TS 中, 是一種泛型約束方式,用于限制一個泛型類型參數(shù) key 的范圍。extends 關(guān)鍵字表示限制 key 的取值只能是 Type 類型中已有的屬性名。可以理解為:Key 只能是 Type 所有鍵中的任意一個,或者說只能訪問對象中存在的屬性。
在上面的例子中,getProp 函數(shù)接收兩個參數(shù):一個泛型類型參數(shù) Type,代表輸入對象的類型;一個泛型類型參數(shù) Key,代表屬性名的類型。keyof Type 實際上獲取的是 person 對象所有鍵的聯(lián)合字面量類型,也就是:&39;name&39; | &39;age&39; | &39;gender&39;,當(dāng)我們調(diào)用調(diào)用 getProp 函數(shù)傳入一個不存在的屬性名,例如: &39;school&39; 會引發(fā)編譯錯誤。
在 TS 中,typeof 操作符的主要用途是在類型上下文中獲取變量或者屬性的類型。
2.1. typeof獲取變量的聲明類型在 TS 中,typeof 可以用來返回一個變量的聲明類型,如果不存在,則獲取該類型的推論類型。
需要注意的是:
- typeof作為類型操作符后面只能跟變量。
- 如果變量沒有聲明類型,typeof返回變量的推斷類型。
如果變量沒有明確聲明類型,typeof 將返回變量的推斷類型。此時,let關(guān)鍵字聲明的變量,可以被重新賦值。
有時候,我們希望變量是常量,不允許被重新賦值。const 關(guān)鍵字可以解決這個問題。此時,基于類型推斷,返回類型是等號右邊的字面量類型。
例如,下面示例中,typeof str 返回的是字面量類型 &39;Echo&39;,不是字符串。
在 Typescript3.4 中引入了一種新的字面量構(gòu)造方式,const 斷言。在 const 斷言作用下,即使是 let 聲明也可以限制類型擴展,變量不能被重新賦值。
例如,下面示例中,typeof str 返回的是字面量類型 &39;Echo&39;,不是字符串。
當(dāng)我們使用 const 斷言構(gòu)造新的字面量表達式時,應(yīng)注意以下幾點:
- 表達式中的任何字面量類型都不應(yīng)該被擴展。
- 對象字面量的屬性,將使用 readonly 修飾。
- 數(shù)組字面量將變成 readonly 元組。
如果變量明確聲明了類型,推斷類型不受 const 影響,typeof str 返回 str 的聲明類型 string,而不是字面量類型 &34;Steven&34;,但是變量依然不能被重新賦值。
typeof與對象結(jié)合使用,可以用來獲取對象的結(jié)構(gòu)類型,以及使用該類型來聲明新的變量或函數(shù)參數(shù)等。
- 獲取對象的類型
在上述示例中,typeof person 返回的是對象 person 的類型,即 { name: string; age: number; }。
- 聲明新變量的類型為對象的類型
在上述示例中,我們使用 typeof person 將 newPerson 的類型聲明為 { name: string; age: number; },并賦予了新的值。
- 在函數(shù)參數(shù)中使用對象的類型
在上述示例中,函數(shù) printObj 接收一個參數(shù),其類型為 typeof person,即接收與對象 person 相同類型的參數(shù)。
需要注意的是,typeof 運算符用于獲取對象類型是在靜態(tài)類型檢查階段進行的,而不是在運行時期執(zhí)行的。因此,它只提供了類型信息,而不會直接訪問對象的值。
2.3. typeof與接口結(jié)合使用typeof 與接口結(jié)合使用可以用于創(chuàng)建新類型,該類型的屬性和方法將與給定對象類型保持一致。
在上述實例中,定義了一個名為 Person 的接口,然后創(chuàng)建一個對象 person,類型為 Person,接著使用 typeof 來創(chuàng)建一個新的類型 NewPerson,該類型的屬性和方法將與 Person 接口中定義的屬性和方法保持一致,這樣我們就可以基于 NewPerson 來創(chuàng)建新的對象。
需要注意的是,typeof 運算符與接口結(jié)合使用通常適用于已存在的對象,它提取已知對象的類型用于創(chuàng)建新的類型。它不會用于動態(tài)創(chuàng)建對象或?qū)嵗悺?/p>2.4. typeof與keyof結(jié)合使用
keyof 主要用于獲取類型的所有屬性鍵,可以與 typeof 結(jié)合使用,獲取某個類型的鍵集合。
在 TS 中,in 操作符的主要用于遍歷目標(biāo)類型的屬性 key 值。類似 for...in,一般結(jié)合 [] 一起使用。
3.1. 遍歷枚舉類型TypeScript 2.8引入了條件類型表達式,類似于三元運算符。
條件類型也支持嵌套。
泛型工具類型這一章節(jié)相關(guān)的內(nèi)容我想放到其它文章中來講,因為這里涉及到的知識點有點多,一時半會寫不完,大家可以持續(xù)關(guān)注我,精力有限,盡量做到每周23更!!!
十五、總結(jié)如果文章有什么錯誤,歡迎大家在評論區(qū)指正,如果覺得本文對您有幫助的話,歡迎關(guān)注點贊收藏哦~
原文鏈接:https://juejin.cn/post/7276630249548005415
- NIGAO集成灶售后服務(wù)電話10-23
-
格力空調(diào)知識講解(如何正確使用和保養(yǎng)格力空調(diào))。
2025-06-07
-
洗衣機馬達怎么試(洗衣機馬達測試方法)
2025-06-07
-
合肥高新區(qū)燃氣灶油煙機維修(如何自行解決常見故障)
2025-06-07
-
貝格爾空氣源熱泵售后維修電話(貝格爾空氣源熱泵售后維修電話)
2025-06-07
-
空調(diào)清洗劑的危害(使用不當(dāng)會對身體造成哪些傷害?)
2025-06-07


