五種在 TypeScript 中使用類型保護(hù)的方法
類型保護(hù)是一個執(zhí)行運行時檢查的表達(dá)式,以保證某個范圍內(nèi)的類型。類型保護(hù)的一個典型應(yīng)用場景是縮小聯(lián)合類型的類型范圍。這是為了保證類型安全,即在運行時安全地訪問特定類型對象中的特定屬性或方法。
在這篇文章中,我將介紹 5 種實現(xiàn)類型保護(hù)的方法。
01、typeof 類型保護(hù)
首先我們來介紹一下比較常見的typeof類型防護(hù)。typeof 運算符可以在運行時獲取對象的類型,該運算符返回以下可能的值:
- "string"
- "number"
- "bigint"
- "boolean"
- "symbol"
- "undefined"
- "object"
- "function"
因此使用 typeof 運算符,我們可以在運行時獲取變量的實際類型。舉個例子:
function printId(id: string | number) {
if (typeof id === "string") {
console.log(`ID: ${id.toUpperCase()}`);
} else if (typeof id === "number") {
console.log(`ID: ${id}`);
}
}
在上面的代碼中,我們定義了一個 printId 函數(shù),它包含一個 id 參數(shù),其類型為字符串 | 數(shù)字聯(lián)合類型。
在函數(shù)體中,我們使用 typeof 運算符來確定參數(shù)的實際類型。如果id參數(shù)是字符串類型,我們會將其值轉(zhuǎn)換為大寫后再輸出。
那么為什么要使用 typeof 運算符來縮小 id 參數(shù)的類型呢?主要原因是為了保證運行時的類型安全。例如,當(dāng)id參數(shù)的類型是數(shù)字類型時,但是我們調(diào)用id.toUpperCase()方法,就會拋出運行時異常。
在支持 TypeScript IntelliSense 的編輯器中,當(dāng)您訪問 id 參數(shù)的某些屬性時,只能訪問字符串和數(shù)字類型的公共屬性。具體如下圖所示:
02、instanceof 類型守衛(wèi)
雖然typeof運算符可以區(qū)分不同的類型,但如果我們想判斷一個對象是否是某個類的實例,從而安全地訪問該實例上特有的屬性或方法,那么typeof運算符就無能為力了。
對于這個需求,我們可以使用instanceof運算符。再次,我們舉一個具體的例子:
class Shape {
constructor(public id: string) {}
}
class Circle extends Shape {
constructor(
public id: string,
public radius: number) {
super(id);
}
}
class Square extends Shape {
constructor(
public id: string,
public sideLength: number) {
super(id);
}
}
在上面的代碼中,我們定義了一個Shape類,并基于它創(chuàng)建了兩個子類。接下來,我們定義一個 printShapeInfo 函數(shù)來打印有關(guān)不同形狀的信息:
function printShapeInfo(shape: Shape) {
if (shape instanceof Circle) {
console.log(`Circle's radius is: ${shape.radius}`);
} else if (shape instanceof Square) {
console.log(`Square's sideLength is: ${shape.sideLength}`);
}
}
在printShapeInfo函數(shù)體中,我們使用instanceof運算符來縮小形狀參數(shù)的類型,從而輸出不同形狀的信息。在 if...else if 分支之外,我們只能訪問 Circle 對象和 Square 對象共有的 id 屬性。
03、in type guards
對于前面使用instanceof運算符實現(xiàn)類型保護(hù)的示例,我們還可以使用接口的形式來描述Shape、Circle和Square類型。
interface Shape {
id: string;
}
interface Circle extends Shape {
radius: number;
}
interface Square extends Shape {
sideLength: number;
}
由于TypeScript接口定義的類型在編譯后并不會生成對應(yīng)的類型,因此我們無法在運行時使用instanceof運算符進(jìn)行類型檢測。要實現(xiàn)printShapeInfo函數(shù)的功能,我們可以使用in運算符,具體實現(xiàn)如下:
function printShapeInfo(shape: Shape) {
if ("radius" in shape) {
console.log(`Circle's radius is: ${shape.radius}`);
} else if ("sideLength" in shape) {
console.log(`Square's sideLength is: ${shape.sideLength}`);
}
}
除了上述方法之外,我們還可以使用可判別聯(lián)合類型來表示Shape類型:
type Circle = {
id: string;
type: "circle";
radius: number;
};
type Square = {
id: string;
type: "square";
sideLength: number;
};
type Shape = Circle | Square;
在Circle和Square類型中,type屬性的類型是字符串文字類型,用于區(qū)分不同的形狀,稱為可區(qū)分屬性。對于判別聯(lián)合類型,結(jié)合switch…case語句,我們還可以實現(xiàn)printShapeInfo函數(shù)對應(yīng)的功能。
function printShapeInfo(shape: Shape) {
switch (shape.type) {
case "circle":
console.log(`Circle's radius is: ${shape.radius}`);
break;
case "square":
console.log(`Square's sideLength is: ${shape.sideLength}`);
break;
default:
console.log("Unknown shape");
}
}
介紹完如何使用常見的 typeof、instanceof 和 in 運算符實現(xiàn)類型保護(hù)之后,我們來介紹一下如何定義用戶自定義的類型保護(hù)。
04、用戶定義的類型保護(hù)
為了演示用戶定義的類型保護(hù),讓我們重新定義 3 種類型:
interface Shape {
id: string;
}
interface Circle extends Shape {
radius: number;
}
interface Square extends Shape {
sideLength: number;
}
定義完Shape相關(guān)的類型后,我們來定義用戶自定義的類型保護(hù)函數(shù):
function isCircle(shape: Shape): shape is Circle {
return "radius" in shape;
}
function isSquare(shape: Shape): shape is Square {
return "sideLength" in shape;
}
與普通函數(shù)相比,自定義類型保護(hù)函數(shù)返回類型謂詞。上面代碼中的 shape is Circle 就是所謂的類型謂詞。謂詞采用parameterName is Type 的形式,其中parameterName 必須是當(dāng)前函數(shù)簽名中的參數(shù)名稱。
這樣就可以理解isCircle用戶自定義類型保護(hù)函數(shù)的作用了。如果函數(shù)返回值為true,則shape參數(shù)的類型為Circle類型。
現(xiàn)在我們有了 isCircle 和 isSquare 函數(shù),我們可以在 printShapeInfo 函數(shù)中使用它們,如下所示:
function printShapeInfo(shape: Shape) {
if (isCircle(shape)) {
console.log(`Circle's radius is: ${shape.radius}`);
} else if (isSquare(shape)) {
console.log(`Square's sideLength is: ${shape.sideLength}`);
}
}
05、相等縮小類型防護(hù)
除了前面描述的 4 種類型保護(hù)方法之外,TypeScript 還支持使用 if/switch 語句和相等檢查,例如 ===、!===、== 和 != 運算符來縮小變量的類型。
function printValues(a: string | number, b: string | string[]) {
if (a === b) {
console.log(a.toUpperCase()); // (parameter) a: string
console.log(b.toUpperCase()); // (parameter) b: string
} else {
console.log(a); // (parameter) a: string | number
console.log(b); // (parameter) b: string | string[]
}
}
上面的代碼中,printValues函數(shù)支持a和b 2個參數(shù),并且它們的類型都是聯(lián)合類型。當(dāng)a===b表達(dá)式的計算結(jié)果為true時,參數(shù)a和b的類型將縮小為字符串類型。當(dāng)然,使用!==運算符也可以用來實現(xiàn)類型縮小。
function printValues2(a: string | number, b: string | string[]) {
if (a !== b) {
console.log(a); // (parameter) a: string | number
console.log(b); // (parameter) b: string | string[]
} else {
console.log(a.toLowerCase()); // (parameter) a: string
console.log(b.toLowerCase()); // (parameter) b: string
}
}
這就是關(guān)于 TypeScript 類型防護(hù)的全部內(nèi)容。
總結(jié)
以上就是我今天想與你分享的5個TS的知識技能,希望對你有所幫助。