TypeScript
TypeScript 是一門基于 JavaScript 之上的編程語言烙如,它解決了 JavaScript 自有的類型系統(tǒng)的不足凶朗,通過使用 TypeScript 這門語言可以大大提高代碼的可靠程度铝噩。
下面重點介紹 JavaScript 自有類型系統(tǒng)的問題以及如何借助一些優(yōu)秀的技術(shù)方案去解決這些問題漂辐。TypeScript 這門語言可以說著此類問題的最終極解決方案泪喊,所以下面會重點介紹,除此之外也會介紹一些其他的技術(shù)方案髓涯。
大致按照以下來介紹:
- 強類型與弱類型(維度:類型安全)
- 靜態(tài)類型與動態(tài)類型(維度:類型檢查)
- JavaScript 自由類型系統(tǒng)的問題
- Flow 靜態(tài)類型檢查方案
- TypeScript 語言規(guī)范與基本應(yīng)用
強類型與弱類型
強類型與弱類型是從類型安全的維度來區(qū)分編程語言袒啼。
強類型:語言層面限制函數(shù)的實參類型必須與形參類型相同。 不允許有任意的隱式類型轉(zhuǎn)換纬纪。
弱類型:語言層面不會限制實參的類型蚓再。允許有任意的隱式類型轉(zhuǎn)換。
靜態(tài)類型與動態(tài)類型
靜態(tài)類型與動態(tài)類型是從類型檢查的維度定義的包各。
靜態(tài)類型:一個變量聲明時它的類型就是明確的摘仅,且聲明過后,它的類型就不允許再修改问畅。
動態(tài)類型:運行階段才能明確變量類型实檀,且變量的類型隨時可以改變。
JavaScript類型系統(tǒng)特征
JavaScript 是弱類型按声,動態(tài)類型膳犹。
JavaScript 的類型的靈活多變,丟失掉了類型系統(tǒng)的可靠性签则。那么 JavaScript 為什么不設(shè)計成 強類型/靜態(tài)類型 的語言呢须床?這和 JavaScript 的背景有關(guān):
早前的 JavaScript 應(yīng)用簡單,就沒有想到會發(fā)展到今天的規(guī)模渐裂,很多時候幾百行代碼甚至幾十行代碼就搞定了豺旬,這種一眼就能夠看到頭的情況下,類型系統(tǒng)就會顯得很多余柒凉。其次族阅,JavaScript 是一門腳本語言,腳本語言是沒有編譯環(huán)節(jié)的膝捞,直接在運行環(huán)境中運行坦刀,設(shè)計成靜態(tài)語言也沒有意義,因為靜態(tài)語言需要在編譯階段做類型檢查,而 JavaScript 沒有這樣一個環(huán)節(jié)鲤遥。所以 JavaScript 選擇成為了這樣一門 弱類型 / 動態(tài)類型的語言沐寺。
放在當(dāng)時的環(huán)境中,這并沒有什么問題盖奈,并且是 JavaScript 的一大優(yōu)勢混坞,而現(xiàn)如今前端的規(guī)模已經(jīng)完全不同,遍地都是大規(guī)模的應(yīng)用钢坦,JavaScript 的代碼也變得越來越復(fù)雜究孕,開發(fā)周期也越來越長,在這種情況下爹凹,JavaScript 的這些優(yōu)勢也就變成了短板厨诸。
看看這門弱類型語言的問題:
- 如果定義一個obj對象
const obj = {}
,緊接著調(diào)用obj.foo()
,obj中并不存在foo方法,但是在語言層面這樣寫是可行的逛万,只是一旦運行就會報錯。也就是說批钠,只有運行階段才能發(fā)現(xiàn)代碼中的類型異常宇植。而且如果不是立即執(zhí)行foo方法,而是在超時調(diào)用中使用埋心,那要等到計時結(jié)束指郁,才能發(fā)現(xiàn)這個錯誤,如果測試的時候沒有測試到拷呆,就會把隱患留到代碼中闲坎。而如果是強類型,這段代碼在語法層面就會報出錯誤茬斧,不用等到運行的時候才能發(fā)現(xiàn)腰懂。 - 假如有一個函數(shù)
function sum(a, b) { return a + b }
,我們希望它來計算兩個數(shù)字相加的結(jié)果,如果sum(100,'100')
项秉,就會計算成字符串拼接后的結(jié)果,和預(yù)期不符,而在強類型中获搏,如果要求傳入數(shù)字很洋,傳入其他類型語法上就行不通。
那我們再看一下強類型的優(yōu)勢:
- 錯誤更早暴露
- 代碼更智能岁诉,編碼更準(zhǔn)確锚沸。例如給一個函數(shù)傳參,想要使用參數(shù)的方法涕癣,因為編輯器無法判斷參數(shù)是什么類型哗蜈,無法給出提示,但是強類型知道參數(shù)的類型,可以給出相應(yīng)的提示恬叹。
- 重構(gòu)更牢靠候生。例如想修改一個對象中的屬性名,但是不知道哪里用到這個屬性绽昼,不能貿(mào)然修改唯鸭,但是強類型的修改后,引用它的地方都會報錯硅确,有的還可以直接定位到位置目溉,就可以有效的重構(gòu)。
- 減少不必要的類型判斷
Flow
Flow 是 JavaScript 的靜態(tài)類型檢查器菱农。2014年由Facebook提出來的工具缭付,可以彌補Javascript弱類型的弊端。通過類型注解的方式給參數(shù)設(shè)定類型循未,檢測異常.
快速上手
- 安裝依賴:
yarn add flow-bin --dev
- 初始化flow配置文件
yarn flow init
- 文件上方加入
// @flow
- 在需要設(shè)置類型的參數(shù)后加上類型陷猫,例如
:number
- 運行
yarn flow
注:因為js語法并沒有
:number
這樣的語法,所以vscode文件會有錯誤的妖,例如標(biāo)記下劃線绣檬,可以通過設(shè)置 - 搜索 JavaScript validate - enable 取消勾選。
使用 flow 注解的文件不是 js 的標(biāo)準(zhǔn)語法嫂粟,所以導(dǎo)致加了注解代碼是無法正常運行的娇未。解決這樣的問題可以使用工具在我們完成編碼過后自動移除注解。現(xiàn)在有兩種解決方案:
- flow-remove-types:官方提供的方案星虹,最簡單最快速零抬。
- 安裝依賴:
yarn add flow-remove-types --dev
- 執(zhí)行
yarn flow-remove-types src -d dist
, src 為要移除注解的目錄,dist 為生成文件所放的目錄宽涌,在dist 中就可以看到移除注解后的文件了平夜。
- 使用babel
- 安裝依賴
yarn add @babel/core @babel/cli @babel/preset-flow --dev
- 創(chuàng)建文件 .babelrc
{
"presets": [ "@babel/preset-flow" ]
}
- 運行
yarn babel src -d dist
如果項目中已經(jīng)使用了babel,建議使用babel的插件來移除注解卸亮。
Flow 開發(fā)工具插件
flow 需要執(zhí)行命令才能看到類型錯誤褥芒,但是我們想在開發(fā)時寫了這個錯誤就直接看到,可以利用插件嫡良。
在vscode 中下周插件 flow language support锰扶。 這樣代碼中使用類型異常直接會有紅色下波浪提示。
Flow 類型推斷
flow 可以根據(jù)我們函數(shù)中使用參數(shù)情況來推斷參數(shù)類型寝受,例如上圖flow根據(jù) a * a 能夠推斷出 a 應(yīng)該為Number 類型坷牛,所以當(dāng)傳入的不是 Number 類型時,會給出下波浪的提示很澄。不過我們還是建議加上類型注解京闰,可以讓代碼有更好的可讀性颜及。
Flow 類型注解
1. flow 除了可以給函數(shù)的參數(shù)標(biāo)記注解,還可以給函數(shù)的返回值標(biāo)記注解,如果沒有返回值或者返回值類型不正確蹂楣,都會提示,對于沒有返回值的函數(shù)俏站,應(yīng)該將返回值的類型標(biāo)記為void
2. flow 也可以給變量標(biāo)記注解,所有的原始數(shù)據(jù)類型都可以標(biāo)記痊土。
需要注意的是肄扎,存放 undefined 需要使用 void let e: void = undefined
3. 數(shù)組類型
- 第一種方法是使用 Array 類型,這種類型需要一個泛型參數(shù)赁酝,用來表示每一個元素的類型犯祠,下面這種就表示 全部由數(shù)字組成的數(shù)組
const arr1: Array<number> = [1, 2, 3]
- 元素類型后邊跟一個數(shù)組的方括號,這種類型可以表示 全部由字符串組成的數(shù)組
const arr2: number[] = [1, '', null]
- 表示一個固定長度的數(shù)組酌呆,可以用類似數(shù)組字面量的方式表示
const arr3: [string, number] = ['', 100]
4. 對象類型
限制變量為對象類型衡载,在類型注解里寫 {}, 在{}里邊添加具體的成員名稱和對應(yīng)的類型限制隙袁,如果需要某個成員是可選的痰娱,可以在這個成員后加一個 ?
const obj1: { foo?: string, bar: number } = { foo: 'string', bar: 123 }
很多時候會把對象當(dāng)作鍵值對集合去使用,這種時候可以使用任意類型的鍵和任意類型的值菩收。如果需要限制鍵和值的類型梨睁,可以使用類似索引器的語法來設(shè)置。
eg:以下這種表示這個對象可以添加任意多個屬性坛梁,但是每個屬性的鍵和值都必須為字符串
const obj3 : { [string]: string } = {}
obj3.key1 = 'value1'
obj3.key2 = 'value2'
5. 函數(shù)類型
前邊講了函數(shù)的參數(shù)的類型限制和函數(shù)的返回值的類型限制而姐,現(xiàn)在補充一點腊凶,函數(shù)作為參數(shù)時划咐,如何進行類型限制。
如上圖钧萍,函數(shù)作為參數(shù)時褐缠,可以用箭頭函數(shù)的方式,給回調(diào)函數(shù)設(shè)置參數(shù)和返回值的類型风瘦。
6.特殊類型
flow還支持幾種特殊類型队魏。
- 字面量類型。
聲明一個變量万搔,它的類型用一個字符串 'foo' 來表示胡桨,那這個變量的值只能是 'foo'。
const a: 'foo' = 'foo'
但是這種字面量類型的一般不會這么用瞬雹,而是配合一個叫聯(lián)合類型的用法昧谊,去組合幾個特定的值。
const type: 'success' | 'warning' | 'danger' = 'success'
例如這個例子酗捌,type 只能是 success呢诬,warning涌哲,danger 中的一個。
- 聯(lián)合類型不僅可以用在字面量上尚镰,也可以用在普通類型上阀圾。
const b: string | number = 'string' // 100
可以利用 type 關(guān)鍵詞做一個單獨的聲明,聲明一個類型用來表示多個類型聯(lián)合過后的結(jié)果狗唉。
type StringOrNumber = string | number
const b: StringOrNumber = 'string' // 100
- maybe 類型
如果一個變量為number類型初烘,那它不能為空的,但是如果想要這個number可以為空敞曹,可以給類型前加一個 ?,表示這個變量除了可以接收number账月,還可以接收 null,undefined澳迫。
const num: ?number = null
- Mixed Any
Mixed 和 Any 類型都是接收所有類型局齿。
區(qū)別:Mixed是強類型,需要通過類型判斷typeof 來操作數(shù)據(jù)橄登。Any是弱類型抓歼,直接操作就可以。
TypeScript
TypeScript 是一門基于 JavaScript 之上的編程語言拢锹,是 JavaScript 的超集谣妻。其實就是在 JavaScript 基礎(chǔ)上加了一些擴展特性,多出來的就是一套強大的類型系統(tǒng)以及對ES6+的支持卒稳。最終會被編譯為原始的 JavaScript蹋半。
TypeScript 相比 flow 功能更加強大,生態(tài)也更加健全充坑,更加完善减江。
缺點:
- 語言本身多了很多概念
- 項目初期,TypeScript會增加一些成本
快速上手
安裝依賴:
yarn add typescript --dev
創(chuàng)建文件 a.ts捻爷,后綴名為 .ts辈灼,ts文件可以完全按照 JavaScript 標(biāo)準(zhǔn)語法編寫代碼,因為 TypeScript 支持ES6+也榄,所以也可以直接在里邊編寫新特性的代碼巡莹。
在文件中寫入以下代碼
const hello = (name) => {
console.log(name)
}
hello('TypeScript')
然后執(zhí)行yarn tsc .\a.ts
,會編譯出js代碼。
給name參數(shù)添加一個類型注解
在這里甜紫,設(shè)定參數(shù) name 為 string 類型降宅,傳入?yún)?shù)是number,vscode默認可以對TypeScript進行提示囚霸,所以會有波浪線來提示腰根。
我們也可以運行yarn tsc .\a.ts
來編譯,會發(fā)現(xiàn)編譯給出錯誤提示邮辽。
配置文件
使用Typescript對整個項目進行編譯唠雕,需要一個配置文件贸营,可以通過命令行添加:
yarn tsc --init
會生成一個叫 tsconfig.json 的文件
可以看到文件中有一些默認配置,我們可以來修改默認配置岩睁,target 配置的是代碼編譯后編譯成哪個版本钞脂,這里是es5,我們也可以改為es2015捕儒,module 表示輸出的代碼是用什么樣的方式進行模塊化冰啃,sourceMap設(shè)為true代表生成一個 map 文件,開啟源代碼映射刘莹,outDir表示生成的文件放在什么目錄下阎毅,rootDir 表示要編譯的是什么目錄下的文件,strict設(shè)為true表示嚴(yán)格模式点弯。
需要注意的是扇调,如果使用 yarn tsc ./a.js 編譯一個文件是不會按照配置文件來編譯的,要直接執(zhí)行 yarn tsc
原始類型
用法和flow是差不多的抢肛,但是需要注意的一點是狼钮,在 TypeScript中,非嚴(yán)格模式下捡絮,任何類型的值都可以設(shè)為 null 和 undefined 的熬芜。或者可以使用另一個專門來控制是否可以為 null 和 undefined 的屬性strictNullChecks福稳。
標(biāo)準(zhǔn)庫
如果配置文件中target設(shè)為 es5涎拉,那類型設(shè)置為 symbol 類型會有錯誤提示,因為es5還沒有 symbol 類型的圆,而target 這里設(shè)置的每一個版本在 typescript 都有一個對應(yīng)的 標(biāo)準(zhǔn)庫鼓拧。標(biāo)準(zhǔn)庫就是內(nèi)置對象所對應(yīng)的聲明。
但是如果我們就是想最后編譯成es5略板,我們可以使用lib選項指定引用的標(biāo)準(zhǔn)庫毁枯,這樣symbol就不會報錯了
但是這樣console.log又會有錯誤提示慈缔,這是因為我們設(shè)置lib將lib的默認值給覆蓋了叮称,只需要再把bom和dom引用回來就好,在TypeScript中DOM和BOM都是用的DOM
作用域問題
使用Typescript在兩個文件中聲明名字一樣的變量時藐鹤,會提示錯誤瓤檐,這是因為他們編譯后都是全局變量,這個時候只要把文件作用域改變就好娱节,變成模塊作用域挠蛉,在文件最后使用export {}
Object類型
TypeScript 中的Object并不特指對象類型,而是指所有的非原始類型肄满。
如果要對象類型的結(jié)構(gòu)谴古,可以使用對象字面量的形式:
對象字面量的形式要求屬性和類型中的成員一一對應(yīng)质涛,不能多不能少,否則就會有錯誤提示掰担。
相比與對象字面量的形式汇陆,更好的方式是使用接口,這個后邊再詳細介紹带饱。
數(shù)組類型
數(shù)組類型和flow一樣可以使用 Array 范式毡代,也可以使用 類型[]
元組類型
元組類型就是明確元素數(shù)量以及元素類型的數(shù)組。
枚舉類型
我們在開發(fā)過程中經(jīng)常遇到需要用某幾個值代表某幾個狀態(tài)勺疼,eg:
=》
在TypeScript中有一個枚舉類型教寂,使用 enum 關(guān)鍵詞聲明一個枚舉,如下圖执庐,這里使用的是 等號 而不是 冒號酪耕。使用這個枚舉和使用對象是一樣的。
這里的枚舉值可以不指定轨淌,默認就是從0開始累加因妇,如果設(shè)置了固定值,比如success設(shè)置了6猿诸,那就從6開始累加婚被。
枚舉的值除了可以是數(shù)字,還可以是字符串梳虽,字符串是無法自增長的址芯,需要手動給每個枚舉設(shè)置一個值。
還有一點需要注意窜觉,枚舉類型會入侵到運行時的代碼谷炸,通俗講就是影響我們編譯后的結(jié)果。TypeScript中的類型檢查在編譯后都會被移除掉禀挫,但是enum類型不會旬陡,它會被編譯為一個雙向的鍵值對對象。我們可以打開終端運行 yarn tsc语婴,打開編譯后的文件描孟,可以看到這樣一個雙向的鍵值對對象。
所謂雙向就是可以通過鍵去獲取值砰左,也可以通過值去獲取鍵匿醒。這樣做的目的是可以讓我們可以動態(tài)的根據(jù)枚舉值獲取枚舉的名稱:PostStatus[0] //success,如果我們確認我們代碼中不會使用枚舉值獲取枚舉名稱缠导,那我們可以常量枚舉廉羔,常量枚舉的用法就是在 enum 前面加一個const:
再次進行編譯,可以看到結(jié)果僻造,鍵值對對象會被去掉憋他,而且使用枚舉的地方會被替換為枚舉值孩饼,枚舉名稱會以注釋的方式放在后面進行標(biāo)注。
函數(shù)類型
- 函數(shù)聲明的方式
在參數(shù)后加類型竹挡,在括號后加類型就是給返回值設(shè)置類型捣辆。這樣設(shè)置的函數(shù)調(diào)用時,必須按照參數(shù)的類型和個數(shù)來調(diào)用此迅。
如果某個參數(shù)是可選的汽畴,就在參數(shù)名稱后加 ?,那這個參數(shù)就可傳可不傳
或者通過設(shè)置默認值的方式耸序,不傳就會取默認值
如果需要接收任意個數(shù)的參數(shù)忍些,可以使用es6的 rest
- 函數(shù)表達式的方式
函數(shù)表達式的方式也可以給參數(shù)和返回值設(shè)置類型,但是接受這個函數(shù)的變量也應(yīng)該有類型坎怪,一般TypeScript都能根據(jù)函數(shù)表達式推斷出這個變量的類型罢坝,不過如果我們是把函數(shù)作為參數(shù)傳遞進去,也就是回調(diào)函數(shù)的方式搅窿,那回調(diào)函數(shù)就必須約束類型嘁酿,就可以使用箭頭函數(shù)的方式約束這個函數(shù)應(yīng)該使用什么樣的類型 func2: (a: number, b: number) => string
任意類型
由于JavaScript是弱類型語言,本身就支持接收任意類型的參數(shù)男应,而TypeScript是基于JavaScript基礎(chǔ)之上的闹司,所以難免在代碼中需要接收一個任意類型的數(shù)據(jù)。那我們可以給它設(shè)置為 any 類型沐飘,any 類型不會為參數(shù)進行類型檢查游桩。
隱式類型推斷
如果我們沒有通過一個注解來標(biāo)記一個變量的類型,TypeScript 會根據(jù)這個變量的使用情況去推斷這個變量的類型耐朴,這種特性叫隱式類型推斷借卧。
這里我們給 age 設(shè)置為18,typescript就會推斷age為number類型筛峭,如果我們在設(shè)置age為一個字符串铐刘,typescript就會給出錯誤提示。這個時候就相當(dāng)于給了age一個類型注解影晓。
如果它無法判斷一個變量的類型镰吵,那它就會標(biāo)記為any。如下圖聲明一個變量foo俯艰,但是沒有給它賦值捡遍,這時候typescript給它類型 any锌订,foo在賦值的時候就可以賦任意類型的值竹握。
盡管typescript可以隱式推斷類型,但還是建議我們?yōu)槊總€變量添加明確的類型辆飘,有利于我們后期更直觀的理解我們的代碼啦辐。
類型斷言
在有些特殊情況下谓传,typescript無法推斷出我們變量的類型,而我們開發(fā)者可以代碼的使用情況是可以明確知道變量是什么類型的芹关。
假如我們有一個數(shù)組const nums = [100, 210, 254, 1552]
续挟,這個數(shù)組我們是從一個接口得到的明確的結(jié)果,我們需要使用find方法找出數(shù)組中第一個大于0的數(shù)字侥衬,const res = nums.find(i => i > 0)
诗祸,很明顯,它的返回值一定是一個數(shù)字轴总,但是typescript并不知道直颅,它推斷出我們的返回值是一個number 或 undefined,它認為我們有可能找不到怀樟。
這時候我們就不能把返回值當(dāng)作數(shù)字來使用功偿。這個時候我們就可以斷言這個res是number類型的,斷言的意思就是明確告訴typescript往堡,你相信我械荷,這個地方一定是number類型的。
類型斷言的方式有兩種:
一種是使用 as 關(guān)鍵詞:
這個時候編輯器就能知道num1是一個數(shù)字了虑灰。
另一種方式是使用 <> 的方式進行斷言:
但是 <> 的方式在 JSX 的語法下會產(chǎn)生沖突吨瞎,就不能使用這種方式了。所以推薦使用 as 關(guān)鍵詞的方式穆咐。
接口 Interfaces
Interfaces(接口)可以理解為一種規(guī)范关拒,契約。它是一種抽象的概念庸娱,可以約定對象的結(jié)構(gòu)着绊,使用一個接口,就必須遵循這個接口全部的約定熟尉,最直觀的就是可以約定一個對象中可以有什么成員归露,這些成員又是什么類型。
可選成員:如果一個對象中的某個成員可有可無斤儿,可以用可選成員這個特性剧包。可選成員只需要給成員后加一個 ? 就可以了
只讀成員:在成員名前面加個readonly就是只讀成員往果,只讀成員在給屬性名賦值后就不可以修改了
動態(tài)成員:比如緩存對象疆液,需要動態(tài)鍵值。因為定義的時候無法知道會有哪些具體的成員陕贮,所以不能指定具體成員名稱堕油,而是使用 [key: string],這里的key不是固定的,可以是任意名稱,只是代表了屬性名稱掉缺。
下圖的用法就規(guī)定了Cache類型的對象必須鍵和值都是字符串卜录。
類的用法
基本使用
Typescript增強了 class 的相關(guān)語法。
constructor 構(gòu)造函數(shù)中使用 this為當(dāng)前屬性賦值會報錯眶明,但是直接使用this訪問當(dāng)前類的屬性會報錯艰毒,這是因為typescript中需要明確在類型當(dāng)中去聲明它所擁有的屬性,而不是通過在構(gòu)造函數(shù)中動態(tài)通過this添加搜囱。
在類型中聲明的方式就是直接在類中定義 name:string丑瞧,在這里也可以為name添加默認值,但是一般不這么用蜀肘,都是在構(gòu)造函數(shù)中為name添加值嗦篱。
在類中聲明的變量必須有默認值,要么聲明的時候直接給默認值幌缝,要么構(gòu)造函數(shù)中賦值灸促,兩者選其一,否則會報錯涵卵。
訪問修飾符
private修飾符:在age屬性前加 private 浴栽,age就變成了私有屬性,只能在內(nèi)部訪問轿偎。當(dāng)在外部訪問的時候就會報錯典鸡。
public修飾符:在name前加 public ,name就是公有成員坏晦,不過不加 public 也默認是公有成員萝玷,所以加和不加是一樣的,但是建議加上 public 修飾符昆婿,代碼會更容易理解
protected修飾符是受保護的球碉,同樣無法在外部訪問到。它和private修飾符的區(qū)別是仓蛆,protected 是只允許在子類訪問的成員睁冬。
constructor 默認也是public類型的,但是如果手動給它加上private看疙,它就變成了私有類型豆拨,無法被外部訪問,也就無法用 new 方法來實例化對象了能庆。這個時候可以創(chuàng)建一個靜態(tài)方法 static create來返回一個實例化的對象施禾,外部就可以通過Student.create()創(chuàng)建實例了:
只讀屬性
可以給屬性設(shè)置只讀屬性 readonly,屬性就不可以被修改了搁胆,不管是在內(nèi)部還是外部弥搞。
需要注意的是只讀屬性如果和修飾符一起用邮绿,要放在修飾符的后面。
類與接口
像這樣的兩個類都實現(xiàn)了同樣的方法拓巧,可以使用接口抽離出來斯碌,利用interface定義一個接口 EatAndRun一死,在類名后使用 implements EatAndRun肛度,這樣兩個類就必須擁有eat和run方法,不然就會報錯投慈。
但是更多時候接口的兩個方法不會同時存在承耿,所以可以一個接口只約束一個能力,讓一個類型同時實現(xiàn)多個接口伪煤。我們可以將EatAndRun 拆成 Eat 和 Run 兩個接口加袋,然后在類型的后邊使用 ‘,’的方式同時使用兩個接口抱既。
抽象類
抽象類也是用來約束子類當(dāng)中必須要有某一個成員职烧。但是抽象類可以包括一些具體的實現(xiàn),而接口只能抽象一個接口防泵,不包含一個具體的實現(xiàn)蚀之。一般比較大的類目都推薦使用抽象類。
使用抽象類的方式就是在類的前面加一個 abstract捷泞,加上以后這個類就不能創(chuàng)建實例了足删,只能夠繼承。
在抽象類中還可以定義抽象方法锁右,使用 abstract 修飾一下失受,需要注意的是抽象方法不需要方法體,當(dāng)父類有抽象方法時咏瑟,子類就要實現(xiàn)這樣一個方法拂到。
泛型
泛型指我們在定義函數(shù),接口码泞,或類的時候沒有指定具體的類型谆焊,在使用的時候才指定具體類型的特征。以函數(shù)為例浦夷,就是指在聲明的時候沒有指定類型辖试,在調(diào)用的時候才指定類型。這樣做的目的就是為了極大程度的復(fù)用我們的代碼劈狐。
我們來創(chuàng)建一個函數(shù) createNumberArray罐孝,這個函數(shù)時用來創(chuàng)建一個指定長度的數(shù)組,并且元素值也都是number肥缔。
function createNumberArray(length: number, value: number): number[] {
// 由于 Array 對象創(chuàng)建的是any類型的數(shù)組莲兢,所以我們可以通過泛型參數(shù)的方式給數(shù)組指定類型
return Array<number>(length).fill(value)
}
const res = createNumberArray(3, 10) // [10,10,10]
但是這個函數(shù)只能創(chuàng)建數(shù)字類型的數(shù)組,如果想要創(chuàng)建string類型的數(shù)組,這個函數(shù)就做不到了改艇。最笨的辦法就是再創(chuàng)建一個生成string類型數(shù)組的方法收班,但是這樣代碼就會有冗余。我們可以使用泛型谒兄,把類型變成一個參數(shù)妄壶,在調(diào)用的時候再傳遞這個類型袜爪。我們定義一個 createArray 的函數(shù),在函數(shù)名后面使用<>, 在<>中使用泛型參數(shù),一般使用 T豪筝,把函數(shù)中不明確的類型都用 T 去代表俊犯,在調(diào)用的時候就可以使用 createArray<string>(3,'f')
這樣的方式生成任意類型的數(shù)組了闪彼。
function createArray<T>(length: number, value: T): T[] {
return Array<T>(length).fill(value)
}
const res = createArray<string>(3, 'f') // ['f','f','f']
類型聲明
實際項目開發(fā)中難免用到一些第三方模塊敬拓,而這些npm模塊并不一定都是通過typescript編寫的,所以它提供的成員就不會有強類型的體驗啊研。
比如lodash模塊在導(dǎo)入的時候就報錯提示找不到類型聲明的文件御滩。
我們先不管它,提取一下模塊的 camelCase 函數(shù)党远,這個函數(shù)的作用就是把字符串轉(zhuǎn)換為駝峰格式削解,所以它的參數(shù)應(yīng)該是一個字符串,但是當(dāng)我們在調(diào)用它的時候并沒有看到它的類型提示
這種情況下就需要單獨的類型聲明麸锉。使用declare 聲明:
import { camelCase } from 'lodash'
declare function camelCase(input: string): string
const r = camelCase('hello world')
有了這個聲明再使用這個函數(shù)就會有對應(yīng)的類型限制了钠绍。
由于typescript的社區(qū)特別強大,絕大多數(shù)常用的npm模塊都提供了對應(yīng)的聲明花沉,我們只需要安裝它所對應(yīng)的聲明模塊就可以了柳爽。模塊提示就給出了對應(yīng)的依賴,所以我們安裝 @types/lodash . 安裝過后這個模塊就會有對應(yīng)的類型提示了碱屁。
目前越來越多的模塊已經(jīng)在內(nèi)部集成了自己的聲明文件磷脯,不需要安裝單獨的聲明模塊了。例如安裝模塊 query-string.這個模塊用來解析url中的query-string字符串娩脾。這個模塊內(nèi)部有自己的生命模塊赵誓,所以可以直接使用類型提示。