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