TypeScript類(lèi)型any never void和unknown使用場(chǎng)景區(qū)別
前言
不知道初學(xué)TypeScript的同學(xué)會(huì)不會(huì)和我一樣被any,unknown,never和void這幾個(gè)類(lèi)型搞得暈頭轉(zhuǎn)向呢?如果你也有同樣的困惑,那么就請(qǐng)跟著本篇文章徹底搞懂這四種類(lèi)型的區(qū)別吧。
any
首先給大家介紹的是any類(lèi)型,我相信一些從JavaScript轉(zhuǎn)TypeScript的同學(xué)一定不會(huì)對(duì)這個(gè)類(lèi)型感到陌生,因?yàn)樗俏覀儗?code>JavaScript代碼重構(gòu)為TypeScript代碼的銀彈,甚至有些程序員由于過(guò)度依賴(lài)any類(lèi)型活生生將TypeScript寫(xiě)成了AnyScript。
用法
當(dāng)我們將某個(gè)變量定義為any類(lèi)型后,TypeScript將會(huì)跳過(guò)對(duì)這個(gè)變量的類(lèi)型檢查:
let something: any = 'Hello World!' something = 18 // ok! ts不會(huì)管any的類(lèi)型檢查 something = true // ok! ts不會(huì)管any的類(lèi)型檢查
在上面的代碼中一般來(lái)說(shuō)如果something被初始化為一個(gè)字符串類(lèi)型后,是不可以被賦值為number或者boolean類(lèi)型的,不過(guò)由于我們聲明了它的類(lèi)型是any所以TypeScript就完全不管這個(gè)對(duì)象的類(lèi)型檢查了。除了這個(gè),你還可以隨意訪(fǎng)問(wèn)這個(gè)any對(duì)象上面的任意屬性,即使它們不存在:
let something: any = 'Hello World!' something.notExistMethod() // ok! something.notExistProperty.name() // ok!
在上面的代碼中我們可以訪(fǎng)問(wèn)any類(lèi)型對(duì)象的任意屬性,并且這個(gè)any是具有傳遞性的,也就是說(shuō)something后面無(wú)論跟了多少個(gè)屬性訪(fǎng)問(wèn),它們的類(lèi)型都是any。
使用場(chǎng)景
any一般的使用場(chǎng)景有下面這些:
- 代碼從JS遷移到TS:這個(gè)時(shí)候使用
any我們可以將重構(gòu)快速推進(jìn)而不用陷入無(wú)邊無(wú)盡的類(lèi)型錯(cuò)誤里面去。這里值得一提的是any只能作為臨時(shí)過(guò)渡方案,我們最后的結(jié)果一定是確保代碼盡可能少any的出現(xiàn) - 我們不關(guān)心對(duì)象的類(lèi)型:例如我們實(shí)現(xiàn)了一個(gè)
print函數(shù),這個(gè)函數(shù)底層調(diào)用console.log,這個(gè)時(shí)候其實(shí)我們不需要關(guān)心傳進(jìn)來(lái)的具體數(shù)據(jù)類(lèi)型是什么,我們只需要一股腦將它傳遞給console.log函數(shù)即可,這個(gè)時(shí)候我們就可以將函數(shù)的參數(shù)類(lèi)型設(shè)置為any了
類(lèi)型缺失或者補(bǔ)全困難:這種情況一般發(fā)生在我們使用了第三方JS編寫(xiě)庫(kù)的時(shí)候,我們沒(méi)有辦法知道某個(gè)導(dǎo)出的函數(shù)的具體類(lèi)型,這時(shí)我們需要定義一些全局的類(lèi)型墊片:
// shim.d.ts
declare module "unknown-type-lib" {
export function someFunction(input: string): any
}
注意:雖然any很方便,可是我們還是需要想辦法避免代碼中出現(xiàn)any類(lèi)型,這是因?yàn)?code>any跳過(guò)了所有的類(lèi)型檢查,而這會(huì)給我們帶來(lái)一些潛在的安全問(wèn)題。最壞的情況是整個(gè)代碼除了any沒(méi)有其它有意義的類(lèi)型,這個(gè)時(shí)候還不如直接編寫(xiě)JavaScript代碼來(lái)得實(shí)在,既然我們選擇了TypeScript,我們還是希望它可以真真實(shí)實(shí)為我們帶來(lái)收益的,因此當(dāng)我們碰到一些很難編寫(xiě)的類(lèi)型時(shí),最好的做法不是寫(xiě)any,而是詢(xún)問(wèn)一些資深的工程師或者網(wǎng)上查找資料來(lái)解決這個(gè)問(wèn)題。
unknown
上面我們說(shuō)到了any會(huì)跳過(guò)所有的類(lèi)型問(wèn)題,而這其實(shí)會(huì)為日后的代碼維護(hù)和開(kāi)發(fā)埋下巨大的安全隱患。為了解決any的問(wèn)題,TypeScript在3.0版本引入了unknown類(lèi)型,它可以理解為類(lèi)型安全的(type-safe)any。
用法
和any一樣,任何類(lèi)型都可以賦值給unknown類(lèi)型的對(duì)象:
let vAny: any = 'Hello World!' // ok! any對(duì)象接受任何類(lèi)型 let vUnknown: unknown = 'Hello World!' // ok! unknown對(duì)象接受任何類(lèi)型的對(duì)象
和any不一樣,unknown類(lèi)型的對(duì)象不可以直接賦值給其它非unknown或any類(lèi)型的對(duì)象,并且不可以訪(fǎng)問(wèn)上面的任何屬性:
let vAny: any = 'Hello World!' let vUnknown: unknown = 'Hello World!' let vNumberForAny: number = vAny // ok! any可以直接賦值給其它任意類(lèi)型 let vNumberForUnknown: number = vUnknown // error! unknown不可以直接賦值給其它非any和unknown類(lèi)型的對(duì)象 vAny.toLocaleLowerCase() // ok! any可以訪(fǎng)問(wèn)所有的屬性 vUnknown.toLocaleLowerCase() // error! unknown對(duì)象不可以直接訪(fǎng)問(wèn)上面的屬性
那么應(yīng)該怎樣才能使用unknown類(lèi)型的變量呢?答案很簡(jiǎn)單,那就是你需要先推斷出對(duì)象的類(lèi)型,才能使用,推斷的方式有很多種,包括typeof和as assertion等其他type guard方法:
let vUnknown: unknown = 'abc'
// 使用typeof推斷出vUnknown的類(lèi)型是string
if (typeof vUnknown === 'string') {
vUnknown.toLocaleUpperCase() // ok! 因?yàn)槟苓M(jìn)入這個(gè)if條件體就證明了vUnknown是字符串類(lèi)型!
}
let vNumberForUnknown: number = vUnknown as number // unknown類(lèi)型一定要使用as關(guān)鍵字轉(zhuǎn)換為number才可以賦值給number類(lèi)型
使用場(chǎng)景
由于unknown基本可以替代any,所以在任何any適用的場(chǎng)景,都應(yīng)該優(yōu)先使用unknown。使用了unknown后,我們既允許某個(gè)對(duì)象儲(chǔ)存任意類(lèi)型的變量,同時(shí)也要求別人在使用這個(gè)對(duì)象的時(shí)候一定要先進(jìn)行類(lèi)型推斷。
never
never不像前面那幾個(gè)類(lèi)型一樣常用,甚至有些同學(xué)可能一開(kāi)始?jí)焊筒恢肋@個(gè)類(lèi)型存在的意義是什么。我們知道TypeScript在解析我們的代碼時(shí)會(huì)對(duì)代碼進(jìn)行類(lèi)型推斷,并且在代碼流不斷深入的時(shí)候,類(lèi)型會(huì)從較為寬泛的類(lèi)型(例如any)一直推斷到較為具體的類(lèi)型,而這么推斷下去是會(huì)有個(gè)終點(diǎn),這個(gè)終點(diǎn)就是不存在的,不可能發(fā)生的類(lèi)型,也就是類(lèi)型系統(tǒng)的底部類(lèi)型(bottom type),而never就是TypeScript的底部類(lèi)型。
用法
never類(lèi)型只接受never類(lèi)型的對(duì)象,甚至萬(wàn)金油any類(lèi)型都不可以賦值給never類(lèi)型。
let vAny: any = 1 let vNever: never = vAny // error! never除了自己誰(shuí)不都接受!
一般當(dāng)我們想表示某個(gè)函數(shù)永遠(yuǎn)不會(huì)返回時(shí),可以使用never類(lèi)型,例如下面的例子:
// 因?yàn)檫@個(gè)是無(wú)限循環(huán),我們可以使用never作為返回值表示它永遠(yuǎn)不會(huì)返回
function foreverLoop(): never {
while(true) {}
}
// 因?yàn)檫@個(gè)函數(shù)會(huì)拋出異常,所以也是不會(huì)返回的
function crashFunc(): never {
throw new Error('this function will crash')
}
使用場(chǎng)景
never類(lèi)型的一個(gè)最大的作用就是幫我們對(duì)類(lèi)型進(jìn)行exclusive check,例如下面這個(gè)例子:
interface QA {
kind: 'qa'
bug: number
}
interface Developer {
kind: 'developer'
hair: number
}
type TechDude = QA | Developer
function printTechDude(h: TechDude) {
if (h.kind === 'qa') {
console.log(h.bug)
} else if (h.kind === 'developer') {
console.log(h.hair)
} else {
let exclusiveCheck: never = h // 由于這個(gè)代碼永遠(yuǎn)也到達(dá)不了,所以h的類(lèi)型被自動(dòng)推斷為never
}
}
上面的代碼現(xiàn)在是沒(méi)有問(wèn)題的,不過(guò)假如某一天我們新增了一個(gè)新的PM類(lèi)型,而忘記在printTechDude函數(shù)里面處理這個(gè)新類(lèi)型的話(huà),上面的代碼會(huì)報(bào)錯(cuò):
interface QA {
kind: 'qa'
bug: number
}
interface Developer {
kind: 'developer'
hair: number
}
interface PM {
kind: 'pm'
features: number
}
// TechDude多了一個(gè)PM類(lèi)型
type TechDude = QA | Developer | PM
function printTechDude(h: TechDude) {
if (h.kind === 'qa') {
console.log(h.bug)
} else if (h.kind === 'developer') {
console.log(h.hair)
} else {
let exclusiveCheck: never = h // error! 因?yàn)镻M類(lèi)型不可以賦值給never類(lèi)型
}
}
上面代碼報(bào)錯(cuò)的原因是TechDude這個(gè)類(lèi)型在else這個(gè)代碼體里面已經(jīng)被TypeScript收攏為PM類(lèi)型,所以不再是never類(lèi)型了。要去掉這個(gè)錯(cuò)誤,我們需要在printTechDude函數(shù)里面額外加多一個(gè)else if(h.kind === 'pm')的判斷:
function printHuman(h: TechDude) {
if (h.kind === 'qa') {
console.log(h.bug)
} else if (h.kind === 'developer') {
console.log(h.hair)
} else if (h.kind === 'pm') {
console.log(h.features)
}else {
let exclusiveCheck: never = h
}
}
也正是因?yàn)?code>exclusive check的存在我們才可以在類(lèi)型變化的時(shí)候及時(shí)發(fā)現(xiàn),避免問(wèn)題留到了線(xiàn)上環(huán)境。
void
void其實(shí)可以理解為null和undefined的聯(lián)合類(lèi)型,它表示空值。
用法
我們一般不會(huì)聲明某個(gè)值的類(lèi)型為void,因?yàn)樗硎具@個(gè)值只能是undefined或者null(strictNullChecks沒(méi)被指定):
let vVoid: void = undefined
void一個(gè)更加常見(jiàn)的使用場(chǎng)景是表示某個(gè)函數(shù)沒(méi)有任何返回值:
function noReturnValue(): void {
console.log('hello')
// 代碼沒(méi)有任何返回值,所以這個(gè)函數(shù)的返回值是void
}
使用場(chǎng)景
這里只想說(shuō)明一下void和never的區(qū)別。void表示空值,也就是null或者undefined,而never則表示永遠(yuǎn)都不會(huì)出現(xiàn)的值。
總結(jié)
本篇文章通過(guò)例子給大家介紹了TypeScript中幾個(gè)容易混淆的類(lèi)型any,unknown,never和 void,希望能幫助有需要的人解答到疑惑,更多關(guān)于TypeScript類(lèi)型使用場(chǎng)景的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Manipulation-TypeScript?DOM操作示例解析
這篇文章主要為大家介紹了DOM?Manipulation-TypeScript?DOM操作示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
postman數(shù)據(jù)加解密實(shí)現(xiàn)APP登入接口模擬請(qǐng)求
對(duì)于Postman的使用,一般情況下只要發(fā)發(fā)確定的請(qǐng)求與參數(shù)就可以的了,然而,在使用的時(shí)候,尤其是接口測(cè)試時(shí),請(qǐng)求接口的設(shè)計(jì)里面都有數(shù)據(jù)加密,參數(shù)驗(yàn)簽,返回?cái)?shù)據(jù)也有進(jìn)行加密的,這個(gè)時(shí)候就需要使用一些腳本做處理,模擬app登入請(qǐng)求的操作2021-08-08
CKEditor4配置與開(kāi)發(fā)詳細(xì)中文說(shuō)明文檔
網(wǎng)上分享的CKEditor4中文說(shuō)明很多都只是的部分使用方法,今天為大家分享一下比較完整的CKEditor4中文說(shuō)明文檔2018-10-10
微信小程序?qū)崿F(xiàn)圖片自適應(yīng)(支持多圖)
這篇文章主要介紹了微信小程序如何實(shí)現(xiàn)圖片自適應(yīng)的相關(guān)資料,文中介紹的方法同樣適應(yīng)于多圖,有需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-01-01
前端算法之TypeScript包含min函數(shù)的棧實(shí)例詳解
這篇文章主要為大家介紹了前端算法之TypeScript包含min函數(shù)的棧實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
TypeScript數(shù)據(jù)結(jié)構(gòu)鏈表結(jié)構(gòu)?LinkedList教程及面試
這篇文章主要為大家介紹了TypeScript數(shù)據(jù)結(jié)構(gòu)鏈表結(jié)構(gòu)?LinkedList教程及面試,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
TypeScript 交叉類(lèi)型使用方法示例總結(jié)
這篇文章主要為大家介紹了TypeScript 交叉類(lèi)型使用方法示例總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
Typescript?extends?關(guān)鍵字繼承類(lèi)型約束及條件類(lèi)型判斷實(shí)現(xiàn)示例解析
這篇文章主要介紹了Typescript?extends?關(guān)鍵字繼承類(lèi)型約束及條件類(lèi)型判斷實(shí)現(xiàn)示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
TypeScript開(kāi)發(fā)HapiJS應(yīng)用詳解
這篇文章主要為大家介紹了TypeScript開(kāi)發(fā)HapiJS應(yīng)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08

