TypeScript高級類型


交叉類型

將多個類型合並成一個類型,取兩個類型的並集。與繼承的區別是,繼承可以有自己的屬性,而交叉沒有。

interface DogInterface {
   run():void
}
interface CatInterface {
   jump():void
}
let pet: DogInterface & CatInterface = { // 看上去和接口多繼承很像,但有一點區別。繼承可以有自己的屬性,交叉不行。
   run(){},
   jump(){},
};

聯合類型

聲明的類型並不確定,可以是多個類型中的一個。

let a: number | string = "a"; // 類型限定
let b: "a" | "b" | "c"; // 限定取值
let c: 1 | 2 | 3 | "v"; // 限定取值

可區分的類型保護:

// 現在有兩種形狀,area函數用來計算每種形狀的面積。
interface Square{
   kind: "square";
   size: number;
}
interface Rectangle{
   kind: "rectangle",
   width: number,
   height: number,
}
type Shape = Square | Rectangle;
function area(s: Shape) {
   switch (s.kind) {
      case "square":
         return s.size * s.size; // 此區塊內,確保只有size屬性
      case "rectangle":
         return s.height * s.width;
   }
}
console.log(area({kind:"square",size:10})); // 100
// 現在要添加一個形狀:圓形。需要定義接口Circle、為Shape添加聯合類型Circle,然后為area函數內增加一個case。但是,如果我們忘了修改area函數,會發生什么?
interface Circle{
   kind: "circle",
   r: number,
}
type Shape = Square | Rectangle | Circle;
console.log(area({kind:"circle",r:10})); // undefined,這里並不報錯,並不符合我們的預期。我們希望bug能夠及時暴露出來,增加程序的穩定性。
做如下改動:
function area(s: Shape) {
   switch (s.kind) {
      case "square":
         return s.size * s.size;
      case "rectangle":
         return s.height * s.width;
      case "circle":
         return Math.PI * s.r;
      default:
         return ((e: any)=>{throw new Error(`沒有定義 ${s} 的面積計算方式`)})(s) // 這一步很重要,一定要在這里拋出異常
   }
}

索引類型

當我們使用不存在的索引時,會返回undefined,沒有約束(如下代碼)。因此我們需要有對索引的約束。

let obj = {
   a: 1,
   b: 2,
   c: 3,
};
function getValue(obj: any,keys: string[]){
   return keys.map(key => obj[key]);
}
console.log(getValue(obj,["a","b"]));
console.log(getValue(obj,["c","f"])); // 會發現,'f'對應的輸出是undefined,沒有約束,需要用到索引類型

下面使用索引類型:

function getValue<T,K extends keyof T>(obj: T, keys: K[]): T[K][] { // T[k][]表示,返回值必須是obj中的值組成的列表
   return keys.map(key => obj[key]); // 此時keys中的元素只能是obj中的鍵
}
console.log(getValue(obj,["a","b"]));
console.log(getValue(obj,["c","f"])); // 這時就會報錯,有了約束 'f' is not in "a" | "b" | "c"

我們來解釋一下:

這里會用到兩個操作符,查詢操作符 keyof T 和 訪問操作符 T[k](看下面示例)。<T, K extends keyof T> 用到了泛型約束,表示K所約束的參數的值只能是T所約束參數數據中的“鍵”。

// keyof T
interface Obj{
   a: number;
   b: string;
}
let key: keyof Obj; // 此時key表示 'a' | 'b'
 
// T[k]
let value: Obj['a'] // number

映射類型

可以從舊的類型生成新的類型。比如,將接口中的所有成員變成只讀、可選。

TS內置了很多映射類型。

interface Obj{
   a: string;
   b: number;
   c: boolean;
}
// 將Obj接口中每個成員變成只讀屬性,生成一個新的接口。
type ReadonlyObj = Readonly<Obj>;  // Readonly是TS內置的映射類型,下同
// Readonly實現原理,利用了索引類型的操作方法
type Readonly<T> = {
   readonly [P in keyof T]: T[P];
}

再如:

// 將所有屬性變成可選
type PartialObj = Partial<Obj>;
// Partial實現原理
type Partial<T> = {
   [P in keyof T]?: T[P];
}

// 獲取原類型的子集
type PickObj = Pick<Obj,'a'|'b'>;
// 等同於
interface PickObj {
   a: string,
   b: number
}

// 將原類型當做新類型的成員
type RecordObj = Record<'x'|'y',Obj>;
// 等同於
interface RecordObj {
   x: Obj,
   y: Obj, 
}

沙發

條件類型

條件類型指由表達式所決定的類型。條件類型使類型具有了不唯一性,增加了語言的靈活性。

例如:

T extends U ? X : Y   表示如果類型T可以被賦值給類型U,name結果就賦予X類型,否則賦予Y類型

再如:

type TypeName<T> =
   T extends string ? string :
   T extends number ? number :
   T extends boolean ? boolean :
   T extends undefined ? undefined :
   T extends Function ? Function :
   object;

type T1 = TypeName<string>;   // T1為字符串類型
type T2 = TypeName<string[]>; // T2為object類型
type T3 = TypeName<Function>; // T3為function類型
type T4 = TypeName<string | string[]>; // T4為 string和object的聯合類型

 可以用來做什么?

(A | B) extends U ? X : Y
解析為(A extends U ? X : Y) | (B extends U ? X : Y)

可以利用這一特性做類型的過濾,例如:

type Diff<T,U> = T extends U ? never : T;

type T5 = Diff< 'a'|'b'|'c', 'a'|'e' >; // 作用是過濾掉第一個參數中的'a' 。T5為 'b' | 'c'聯合類型
解析過程:
 Diff<'a', 'a'|'e'> | Diff<'b', 'a'|'e'> | Diff<'c', 'a'|'e'>
 never | 'b' | 'c'
 'b' | 'c'

TS內置的條件類型:

Exclude<T, U>  // 從T中剔除可以賦值給U的類型,相當於上面例子中的Diff
Extract<T, U>  // 提取T中可以賦值給U的類型。
NonNullable<T>  // 從T中剔除null和undefined。
ReturnType<T>   // 獲取函數返回值類型。
InstanceType<T>  // 獲取構造函數類型的實例類型。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM