這段時(shí)間在跟著嗶哩嗶哩https://www.bilibili.com/video/BV14Z4y1u7pi?p=44&spm_id_from=333.880.my_history.page.click&vd_source=e30a43929635b513abdc7ac6a69a0e5b學(xué)習(xí)TypeScript記錄的筆記現(xiàn)在分享給大家。
1.TypeScript介紹
1.1TypeScript是什么
TypeScript(簡(jiǎn)稱:TS)是JavaScript的超集(JS有的TS都有)。
TypeScript = Type+JavaScript(在JS基礎(chǔ)之上,為JS添加了類型支持)。
TypeScript 是微軟開發(fā)的開源編程怨言,可以在任何運(yùn)行JavaScript的地方運(yùn)行
1.2TypeScript為什么要為JS添加類型支持?
背景:JS的類型系統(tǒng)存在"先天缺陷",JS代碼中絕大部分錯(cuò)誤都是類型錯(cuò)誤(Uncaught TypeError)。
問(wèn)題:添加了找Bug、改Bug的時(shí)間,嚴(yán)重影響開發(fā)效率。
從變成語(yǔ)言的動(dòng)靜來(lái)區(qū)分,TypeScript屬于靜態(tài)類型的編程語(yǔ)言,JS屬動(dòng)態(tài)的變成語(yǔ)言。
靜態(tài)類型:編譯期做類型檢查;動(dòng)態(tài)類型:執(zhí)行期做類型檢查
代碼編譯和代碼執(zhí)行的順序:1編譯 2執(zhí)行
對(duì)JS來(lái)說(shuō):需要等待代碼真正去執(zhí)行的時(shí)候才能發(fā)現(xiàn)錯(cuò)誤(晚)
對(duì)TS來(lái)說(shuō):在編譯的時(shí)候(代碼執(zhí)行前就可以發(fā)現(xiàn)錯(cuò)誤)(早)
并且,配合VSCode等開發(fā)工具,TS可以提前到在編寫代碼的同時(shí)就發(fā)現(xiàn)代碼中的錯(cuò)誤,減少Bug、改Bug時(shí)間。
1.3TypeScript相比JS的優(yōu)勢(shì)
- 更早(寫代碼的同時(shí))發(fā)現(xiàn)錯(cuò)誤,減少找Bug、改Bug時(shí)間,提升開發(fā)效率。
- 程序中任何位置的代碼都有提示,隨時(shí)隨地的安全感,增強(qiáng)了開發(fā)體驗(yàn)。
- 強(qiáng)大的類型系統(tǒng)提升了代碼的可維護(hù)性,使得重構(gòu)代碼更加容易。
- 支持最新的ECMAScript語(yǔ)法,優(yōu)先體驗(yàn)最新的語(yǔ)法,讓你走在前端技術(shù)的最前沿。
- TS類型判斷機(jī)制,不需要再代碼中的每個(gè)地方都演示標(biāo)注類型,讓你在享受優(yōu)勢(shì)的同時(shí),盡量降低來(lái)了成本。
除此之外,Vue3源碼使用TS重寫、Angular默認(rèn)支持TS、React與TS完美配合,TypeScript已成為大中型前端項(xiàng)目的首先編程語(yǔ)言。
2.TypeSCript初體驗(yàn)
2.1安裝編譯TS的工具包
問(wèn)題:為什么要安裝編譯TS的工具包?
回答:Node.js/瀏覽器,只認(rèn)識(shí)JS代碼,不認(rèn)識(shí)TS代碼。需要將TS代碼轉(zhuǎn)化為JS代碼,然后才能運(yùn)行。
安裝命令:npm i -g typescript
typescript包:用來(lái)編譯TS代碼的包,提供了tsc命令,實(shí)現(xiàn)了TS->JS的轉(zhuǎn)化。
驗(yàn)證是否安裝成功:tsc -v(查看typescript的版本)。
2.2編譯并運(yùn)行TS代碼
- 創(chuàng)建hello.ts文件(注意:TS文件的后綴名為.ts)。
- 將TS編譯為JS:在終端中輸入命令個(gè),tsc hello.ts(此時(shí),在同級(jí)目錄中會(huì)出現(xiàn)一個(gè)同名的JS文件)。
- 執(zhí)行JS代碼:在終端中輸入命令,node hello.js
說(shuō)明:所有合法的JS代碼都是TS代碼,有JS基礎(chǔ)只需要學(xué)習(xí)TS類型即可
注意:由TS編譯生成的JS文件,代碼中就沒(méi)有類型信息了。
2.3簡(jiǎn)化運(yùn)行TS的步驟
問(wèn)題描述:每次修改代碼后,都要重復(fù)執(zhí)行兩個(gè)命令,才能運(yùn)行TS代碼,太繁瑣。
簡(jiǎn)化方式:使用ts-node包,直接在Node.js中執(zhí)行TS代碼。
安裝命令:npm i -g ts-node(ts-node包提供了ts-node命令)。
使用方式:ts-node hello.ts。(遇到錯(cuò)誤了:在安裝完ts-node之后執(zhí)行這個(gè)使用命令會(huì)報(bào)錯(cuò),原因是缺少安裝東西,執(zhí)行命令 npm install -g tslib @types/node)
解釋:ts-node命令在內(nèi)部偷偷的將TS->JS,然后,再運(yùn)行JS代碼。
3.TypeScript常用類型
概述
TypeScript是JS的超集,TS提供了JS的所有功能,并且額外的增加了:類型系統(tǒng)。
所有的JS代碼都是TS代碼
JS有類型(比如:number/string等) ,但是JS不會(huì)檢查變量的類型是否發(fā)生變化。而TS會(huì)檢查。TypeScript類型系統(tǒng)的主要優(yōu)勢(shì):可以顯示標(biāo)記出代碼中的意外行為,從而降低了發(fā)生錯(cuò)誤的可能性。
3.1.類型注解
實(shí)例代碼:
let age:number = 18
說(shuō)明:代碼中的:number就是類型注解。
作用:為變量添加類型約束,比如,上述代碼中,約定變量age的類型為number(數(shù)值類型)。
解釋:約定了什么類型,就只能給變量賦值該類型的值,否則,就會(huì)報(bào)錯(cuò)。
3.2常用基礎(chǔ)類型概述
可以將TS中的常用基礎(chǔ)類型細(xì)分為兩類:
-
JS已有類型
原始類型:number/string/boolean/null/undefined/symbol
對(duì)象類型:Object(包括,數(shù)組、對(duì)象、函數(shù)等對(duì)象)。
-
TS新增類型
聯(lián)合類型、自定義類型(類型別名)、接口、元祖、字面量類型、枚舉、void、any等
3.3原始類型
1.原始類型:number/string/boolean/null/undefined/symbol
特點(diǎn):簡(jiǎn)單。這些類型,完全按照J(rèn)S中類型的名稱來(lái)書寫
let age: number = 18
let myName: string = "啦啦啦"
let isLoading: boolean = false
let a: null = null
let b: undefined = undefined
let s: symbol = Symbol()
3.4數(shù)組類型
2.對(duì)象類型:Object(包括,數(shù)組、對(duì)象、函數(shù)等對(duì)象)。
特點(diǎn):對(duì)象類型,在TS中更加細(xì)化,每個(gè)具體的對(duì)象都有自己的類型語(yǔ)法。
數(shù)組類型的兩種寫法:(推薦使用number[]寫法)
let numbers: number[] = [1,2,3]
let string: Array<string> = ["a","b","c"]
需求:數(shù)組中既有number類型,又有string類型,這個(gè)數(shù)組的類型應(yīng)該如何寫?
let arr: (number | string)[] = [1,'a','c',2]
解釋:| (豎線) 在TS中叫做聯(lián)合類型(由兩個(gè)或多個(gè)其他類型組成的類型,表示可以是這些類型中的任意一種)。
注意:這是TS中聯(lián)合類型的語(yǔ)法,只有一根豎線,不要與JS中的或(||)混淆了。
3.5類型別名
類型別名:(自定義類型):為任意類型起別名。
使用場(chǎng)景:當(dāng)同一類型(復(fù)雜)被多次使用時(shí),可以通過(guò)類型別名,簡(jiǎn)化該類型的使用。
type CustomArray = (number | string)[]
let arr1: CustomArray = [1,'a',3,'b']
let arr2: CustomArray = ['x','y',6,7]
解釋:
使用type關(guān)鍵字來(lái)創(chuàng)建類型別名。
類型別名(比如,此處的CustomArray),可以是任意合法的變量名稱。
創(chuàng)建類型別名后,直接使用該類型別名作為變量的類型注解即可。
3.6 函數(shù)類型
函數(shù)的類型實(shí)際上指的是:函數(shù)參數(shù)和返回值的類型。
為函數(shù)指定類型的兩種方式:1單獨(dú)指定參數(shù)、返回值的類型 2同時(shí)指定參數(shù)、返回值的類型
3.6.1.單獨(dú)指定參數(shù)、返回值的類型:
function add(num1: number,num2: number):number{
return num1 + num2
}
// 函數(shù)表達(dá)式的方式
const add = (num1: number,num2: number):number =>{
return num1 + num2
}
3.6.2.同時(shí)指定參數(shù)、返回值的類型:
type CustomArray = (number | string)[]
let arr1: CustomArray = [1,'a',3,'b']
let arr2: CustomArray = ['x','y',6,7]
解釋:當(dāng)函數(shù)作為表達(dá)式時(shí),可以通過(guò)類似箭頭函數(shù)形式的語(yǔ)法來(lái)為函數(shù)添加類型。
注意:這種形式只適用于函數(shù)表達(dá)式。
3.6.3.void類型
如果函數(shù)沒(méi)有返回值,那么,函數(shù)返回值類型為:void。
function greet(name: string): void{
console.log("hello")
}
3.6.4.可選參數(shù)
使用函數(shù)實(shí)現(xiàn)某個(gè)功能時(shí),參數(shù)可以傳也可以不傳。這種情況下,在給函數(shù)參數(shù)指定類型時(shí),就用到可選參數(shù)了。比如,數(shù)組的slice方法,可以slice()也可以slice(1)還可以slice(1,3)。
function mySlice(start?:number,end?:number):void{
console.log('起始索引:',start,'結(jié)束索引:',end)
}
可選參數(shù):在可傳可不傳的參數(shù)名稱后面添加?(問(wèn)號(hào))。
注意:可選參數(shù)只能出現(xiàn)在參數(shù)列表后面,也就是說(shuō)可選參數(shù)后面不能再出現(xiàn)必選參數(shù)。
3.7對(duì)象類型
JS中的對(duì)象是由屬性和方法構(gòu)成的,而TS中對(duì)象的類型就是再描述對(duì)象的結(jié)構(gòu)(有什么類型的屬性和方法)。
對(duì)象類型的寫法:
let person:{name:string;age:number;sayHi():void;greet(name:string):void}={
name:'jack',
age:19,
sayHi(){},
green(name){}
}
let person:{//多行的話可以省略;(分號(hào))
name:string
age:number
sayHi():void
sayHi1:()=>void //箭頭函數(shù)的寫法
greet(name:string):void
}={
name:'jack',
age:19,
sayHi(){},
sayHi1(){},
green(name){}
}
解釋:
直接使用{}來(lái)描述對(duì)象結(jié)構(gòu)。屬性采用屬性名:類型的形式;方法采用方法名():返回值類型的形式。
如果方法有參數(shù),就在方法名后面的小括號(hào)中指定參數(shù)類型(比如:greet(name:string):void)。
在一行代碼中指定對(duì)象的對(duì)個(gè)屬性類型時(shí),使用;(分號(hào))來(lái)分隔。
如果一行代碼只指定一個(gè)屬性類型(通過(guò)換行來(lái)分隔多個(gè)屬性類型),可以去掉;(分號(hào))。
方法的類型也可以使用箭頭函數(shù)形式(比如:{sayHi:()=>void})。
對(duì)象的屬性或方法,也可以是可選的,此時(shí)就用到可選屬性了
比如,我們?cè)偈褂胊xios({...})時(shí),如果發(fā)送GET請(qǐng)求,method屬性就可以省略。
function myAxios(config:{url:string;method?:string}){
console.log(config)
}
可選屬性的語(yǔ)法與函數(shù)可選參數(shù)的語(yǔ)法一致,都使用?(問(wèn)號(hào))來(lái)表示。
3.8接口
當(dāng)一個(gè)對(duì)象類型被多次使用時(shí),一般會(huì)使用接口(interface)來(lái)描述對(duì)象的類型,達(dá)到服用的目的。
interface IPerson{
name:string
age:number
sayHi():void
}
let person:IPerson={
name:'jack',
age:19,
sayHi(){}
}
解釋:
1.使用interface關(guān)鍵詞來(lái)聲明接口。
2.接口名稱(比如,此處的IPerson),可以是任意合法的變量名稱。
3.聲明接口后,直接使用接口名稱作為變量的類型。
4.因?yàn)槊恳恍兄挥幸粋€(gè)屬性類型,因此,屬性類型后沒(méi)有;(分號(hào))。
interface(接口)和type(類型別名)的對(duì)比:
相同點(diǎn):都可以給對(duì)象指定類型。
不同點(diǎn):1.接口,只能為對(duì)象指定類型。2.類型別名,不僅可以為對(duì)象指定類型,實(shí)際上可以為任意類型指定別名。
interface IPerson {
name:string
age:number
sayHi():void
}
type IPerson = {
name:string
age:number
sayHi():void
}
type NumStr = number | string
接口之間的繼承
如果兩個(gè)接口之間有相同的屬性或方法,可以將公共的屬性或方法抽離出來(lái),通過(guò)繼承來(lái)實(shí)現(xiàn)復(fù)用。
比如,這兩個(gè)接口都有x、y兩個(gè)屬性,重復(fù)寫兩次,可以,但是很繁瑣。
interface Point2D{x:number;y:number}
interface Point3D{x:number;y:number;z:number}
更好的方式:
interface Point2D{x:number;y:number}
interface Point3D extends Point2D {z:number}
解釋:
使用extends(繼承)關(guān)鍵字實(shí)現(xiàn)接口Point3D繼承Point2D。
繼承后,Point3D就有了Point2D的所有屬性和方法(此時(shí),Point3D同時(shí)有x、y、z三個(gè)屬性)。
3.9元組
場(chǎng)景:在地圖中,使用經(jīng)緯度坐標(biāo)來(lái)標(biāo)記位置信息。
可以使用數(shù)組來(lái)記錄坐標(biāo),那么,該數(shù)組中只有兩個(gè)元素,并且這兩個(gè)元素都是數(shù)值類型。
let position: number[] = [39.5427, 116.2317]
使用number[]的缺點(diǎn):不嚴(yán)謹(jǐn),因?yàn)樵擃愋偷臄?shù)組中可以出現(xiàn)任意多個(gè)數(shù)字。
更改的方式:元組(Tuple)。
元組類型是另一種類型的數(shù)組。它確切地知道包含多少元素,以及特定索引對(duì)應(yīng)的類型。
let position: [number, number] = [39.5427, 116.2317]
解釋:
元組類型可以確切地標(biāo)記處有多少元素,以及每個(gè)元素的類型。
該示例中,元素有兩個(gè)元素,每個(gè)元素的類型都是number
3.10類型推論
在TS中,某些沒(méi)有明確指出類型的地方,TS的類型推論機(jī)制會(huì)幫助提供類型。
換句話說(shuō):由于類型推論的存在,這些地方,類型注解可以省略不寫!
發(fā)生類型推論的2中常見(jiàn)場(chǎng)景:1聲明變量并初始化時(shí) 2決定函數(shù)返回值時(shí)。
let age = 18
//鼠標(biāo)移入變量名age TS自動(dòng)推斷出變量age為number類型
function add(num1:number,num2:number){return num1 + num2}
//TS會(huì)自動(dòng)提示返回值類型時(shí)number
注意:這兩種情況下,類型注釋可以省略不寫!
推薦:能省略類型注解的地方就省略(不是偷懶,充分利用TS類型推論的能力,提升開發(fā)效率)。
技巧:如果不知道類型,可以通過(guò)鼠標(biāo)放在變量名稱上,利用VSCode的提示來(lái)查看類型。
3.11類型斷言
有時(shí)候你會(huì)比TS更明確一個(gè)值的類型,此時(shí),可以使用類型斷言來(lái)指定更具體的類型
比如
<a id="link">百度</a>
<script>
const aLink = document.getElementById('link');
//鼠標(biāo)移動(dòng)上去 顯示const aLink: HTMLElement
</script>
注意:getElementById方法返回值的類型時(shí)HTMLElement,該類型只包含所有標(biāo)簽公共的屬性或方法,不包含a標(biāo)簽特有href等屬性。
因此,這個(gè)類型太寬泛(不具體),無(wú)法操作href等a標(biāo)簽特有的屬性或方法。
解決方式:這種情況下就需要使用類型斷言指定更加具體的類型。
使用類型斷言:
<a id="link">百度</a>
<script>
const aLink = document.getElementById('link') as HTMLAnchorElement;
//鼠標(biāo)移動(dòng)上去 顯示const aLink: HTMLElement
</script>
解釋:
使用as關(guān)鍵字實(shí)現(xiàn)類型斷言。
關(guān)鍵字as后面的類型時(shí)一個(gè)更加具體的類型(HTMLAnchorElement是HTMLElement的子類型)。
通過(guò)類型斷言,aLink的類型變得更加具體,這樣就可以訪問(wèn)a標(biāo)簽特有的屬性或方法了。
另一種語(yǔ)法,使用<>語(yǔ)法,這種語(yǔ)法形式不常用知道即可:
const aLink = <HTMLAnchorElement>document.getElementById('link');
// ***** 這個(gè)方法不是很常用 在寫react中這個(gè)寫法跟jsx語(yǔ)法沖突 不能使用。
技巧:在瀏覽器控制臺(tái),通過(guò)console.dir()打印DOM元素,在屬性列表的后面,即可看到該元素的類型。
3.12字面量類型
思考一下一下代碼,另個(gè)變量的類型分別是什么?
let str1 = "hello"
const str2 = "hello"
通過(guò)TS類型推論機(jī)制,可以得到答案:
變量str1的類型為string。
變量str2的類型為'hello'。
解釋:
str1是一個(gè)變量(let),它的值可以是任意字符串,所以類型為:string。
str2是一個(gè)常量,它的值不能變化只能是'hello',所以,它的類型為'hello'。
注意:此處的"hello",就是一個(gè)字面量類型。也就是說(shuō)某個(gè)特定的字符串也可以作為TS中的類型。除字符串外,任意的JS字面量(比如,對(duì)象,數(shù)字等)都可以作為類型使用。
使用模式:字面量類型配合聯(lián)合類型一起使用。
使用場(chǎng)景:用來(lái)表示一組明確地可選值列表。
比如,在貪吃蛇游戲中,游戲的方向的可選值只能是上下左右中的任意一個(gè)。
function changDirection(direction:'up' | 'down' | 'left' | 'right'){
console.log(direction)
}
解釋:參數(shù)direction的值只能是up/down/left/right中的任意一個(gè)。
優(yōu)勢(shì):相比于string類型,使用字面量類型更加精確、嚴(yán)謹(jǐn)。
3.13枚舉
枚舉的功能類似于字面量+聯(lián)合類型組合的功能,也可以表示一組明確的可選值。
枚舉:定義一組命名常量。它描述一個(gè)值。該值可以是這些命名常量中的一個(gè)。
enum Direction {up,Down,Left,Right}
function changeDirection(direction:Direction){
console.log(direction)
}
解釋:
使用enum關(guān)鍵字定義枚舉。
約定枚舉名稱、枚舉中的值以大寫字母開頭。
枚舉中的多個(gè)值之間通過(guò),(逗號(hào))分隔。
定義好枚舉后,直接使用枚舉名稱作為類型注解。
注意:參數(shù)direction的類型為枚舉Direction,那么,實(shí)參的值就應(yīng)該是枚舉Direction成員的任意一個(gè)。
訪問(wèn)枚舉成員:
enum Direction {Up,Down,Left,Right}
function changeDirection(direction:Direction){
console.log(direction)
}
changeDirection(Direction.Up)
解釋:類似于JS中的對(duì)象,直接通過(guò)點(diǎn)(.)語(yǔ)法訪問(wèn)枚舉成員。
問(wèn)題:我們把枚舉成員作為了函數(shù)的實(shí)參,他的值是什么呢?
changeDirection(Direction.Up);
// 鼠標(biāo)移動(dòng)上去顯示(enum atemember)Direction.Up = 0
解釋:通過(guò)將鼠標(biāo)移入Direction.Up,可以看到枚舉成員Up的值為0。
注意:枚舉成員是有值的,默認(rèn)為:從0開始自增的數(shù)值。
我們把,枚舉成員的值為數(shù)字的枚舉,稱為:數(shù)字枚舉。
當(dāng)然,也可以給枚舉中的成員初始化值。
// Down -> 11, Left -> 12 ,Right -> 13
enum Direction { Up =10, Down, Left, Right }
enum Direction { Up = 2, Down = 4, Left = 8, Right = 16 }
3.13.1 字符串枚舉
字符串枚舉:枚舉成員的值是字符串。
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
注意:字符串枚舉沒(méi)有自增長(zhǎng)行為,因此,字符串枚舉的每個(gè)成員必須有初始值。
3.13.2 枚舉特性和實(shí)現(xiàn)原理
枚舉是TS為數(shù)不多的非JavaScript類型級(jí)擴(kuò)展(不僅僅是類型)的特性之一。
因?yàn)椋浩渌愋蛢H僅被當(dāng)做類型,而枚舉不僅僅作用類型,還提供值(枚舉成員都是有值的)。
也就是說(shuō),其他的類型會(huì)在編譯為JS代碼時(shí)自動(dòng)移除。但是,枚舉類型會(huì)被編譯為JS代碼!
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
//===>編譯后
var Direction;
(function(Direction){
Direction["Up"] = "UP":
Direction["Down"] = "DOWN":
Direction["Left"] = "LEFT":
Direction["Right"] = "RIGHT":
})(Direction || (Direction = {}))
說(shuō)明:枚舉與前面講到的字面量類型+聯(lián)合類型組合的功能類似,都用來(lái)表示一組明確的可選值列表。
一般情況下,推薦使用字面量類型+聯(lián)合類型組合的方式,因?yàn)橄啾让杜e,這種方式更加直觀、簡(jiǎn)潔、高效。
3.14 any類型
原則:不推薦使用any !這回讓TypeScript變?yōu)?AnyScript"(失去TS類型保護(hù)的優(yōu)勢(shì))。
因?yàn)楫?dāng)值得類型為any時(shí),可以對(duì)該值進(jìn)行任意操作,并且不會(huì)有代碼提示。
let obj:any ={x:0}
obj.bar=100
obj()
const n:number = obj
解釋:以上操作都不會(huì)有任何類型錯(cuò)誤提示,即使可能存在錯(cuò)誤!
盡可能的避免使用any類型,除非臨時(shí)使用any來(lái)"避免"書寫很長(zhǎng)、很復(fù)雜的類型!
其他隱式具有any類型的情況:
聲明變量不提供類型也不提供默認(rèn)值
函數(shù)參數(shù)不添加類型
注意:因?yàn)椴煌扑]使用any,所以,這兩種情況下都應(yīng)該提供類型!
3.15typeof
中所周知,JS中提供了typeof操作父,用來(lái)在JS中獲取數(shù)據(jù)的類型。
console.log(typeof "hello world")
實(shí)際上,ts也提供了typeof操作符:可以再類型上下文中引用變量或?qū)傩缘念愋停愋筒樵儯?/p>
使用場(chǎng)景:根據(jù)已有變量的值,獲取該值得類型,來(lái)簡(jiǎn)化類型書寫。
let p={x:1,y:2}
function formatPoint(point:{x:number;y:number}){}
formatPoint(p)
function formatPoint(point:type p){}
解釋:
使用typeof操作符來(lái)獲取變量p的類型,結(jié)果與第一種(對(duì)象字面量形式的類型)相同。
typeof出現(xiàn)在類型注解的位置(參數(shù)名稱的冒號(hào)后面)所處的環(huán)境就在類型上下文(區(qū)別于JS代碼)。
注意:typeof只能用來(lái)查詢變量或?qū)傩缘念愋?,無(wú)法查詢其他形式的類型(比如函數(shù)調(diào)用的類型)。