欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

TypeScript中的類型運(yùn)算符實現(xiàn)

 更新時間:2023年10月27日 10:28:59   作者:海闊天空BM  
TypeScript 是一種強(qiáng)類型語言,它通過使用類型運(yùn)算符來強(qiáng)化類型安全性,本文主要介紹了TypeScript中的類型運(yùn)算符實現(xiàn),感興趣的可以了解一下

1. keyof運(yùn)算符

1. 簡介

是一個單目運(yùn)算符,接受一個對象類型作為參數(shù),返回該對象的所有鍵名組成的聯(lián)合類型。

type MyObj = {
  foo: number,
  bar: string,
};

type Keys = keyof MyObj; // 'foo'|'bar'

這個例子keyof MyObj返回MyObj的所有鍵名組成的聯(lián)合類型,即'foo'|'bar'

由于 JavaScript 對象的鍵名只有三種類型,所以對于任意對象的鍵名的聯(lián)合類型就是string|number|symbol。

對于沒有自定義鍵名的類型使用 keyof 運(yùn)算符,返回never類型,表示不可能有這樣類型的鍵名

type KeyT = keyof object;  // never

上面示例中,由于object類型沒有自身的屬性,也就沒有鍵名,所以keyof object返回never類型。

由于 keyof 返回的類型是string|number|symbol,如果有些場合只需要其中的一種類型,那么可以采用交叉類型的寫法。

type Capital<T extends string> = Capitalize<T>;

type MyKeys<Obj extends object> = Capital<keyof Obj>; // 報錯
type MyKeys<Obj extends object> = Capital<string & keyof Obj>;

這個列子中,string & keyof Obj等同于string & string|number|symbol進(jìn)行交集運(yùn)算,最后返回string,因此Capital<T extends string>就不會報錯了。

如果對象屬性名采用索引形式,keyof 會返回屬性名的索引類型。

// 示例一
interface T {
  [prop: number]: number;
}

// number
type KeyT = keyof T;

// 示例二
interface T {
  [prop: string]: number;
}

// string|number
type KeyT = keyof T;

上面的示例二,keyof T返回的類型是string|number,原因是 JavaScript 屬性名為字符串時,包含了屬性名為數(shù)值的情況,因為數(shù)值屬性名會自動轉(zhuǎn)為字符串

如果 keyof 運(yùn)算符用于數(shù)組或元組類型,得到的結(jié)果可能出人意料。

type Result = keyof ['a', 'b', 'c'];
// 返回 number | "0" | "1" | "2"
// | "length" | "pop" | "push" | ···

上面示例中,keyof 會返回數(shù)組的所有鍵名,包括數(shù)字鍵名和繼承的鍵名。

對于聯(lián)合類型,keyof 返回成員共有的鍵名。

type A = { a: string; z: boolean };
type B = { b: string; z: boolean };

// 返回 'z'
type KeyT = keyof (A | B);

對于交叉類型,keyof 返回所有鍵名。

type A = { a: string; x: boolean };
type B = { b: string; y: number };

// 返回 'a' | 'x' | 'b' | 'y'
type KeyT = keyof (A & B);

// 相當(dāng)于
keyof (A & B) ≡ keyof A | keyof B

keyof 取出的是鍵名組成的聯(lián)合類型,如果想取出鍵值組成的聯(lián)合類型,可以像下面這樣寫。

type MyObj = {
  foo: number,
  bar: string,
};

type Keys = keyof MyObj;

type Values = MyObj[Keys]; // number|string

上面示例中,Keys是鍵名組成的聯(lián)合類型,而MyObj[Keys]會取出每個鍵名對應(yīng)的鍵值類型,組成一個新的聯(lián)合類型,即number|string

2. keyof運(yùn)算符的用途

  • 往往用于精確表達(dá)對象的屬性類型
  • 用于屬性映射,即將一個類型的所有屬性逐一映射成其他值

2. in運(yùn)算符

在js中in用來確定對象是否包含某個屬性名,在ts 類型運(yùn)算中,in運(yùn)算符用來取出(遍歷)聯(lián)合類型的每一個成員類型。

type U = 'a'|'b'|'c';

type Foo = {
  [Prop in U]: number;
};
// 等同于
type Foo = {
  a: number,
  b: number,
  c: number
};

[Prop in U]表示依次取出聯(lián)合類型U的每一個成員。

3. 方括號運(yùn)算符

用來取出對象的鍵值類型,比如T[K]會返回對象T的屬性K的類型。

type Person = {
  age: number;
  name: string;
  alive: boolean;
};
// Age 的類型是 number
type Age = Person['age'];

方括號的參數(shù)如果是聯(lián)合類型,那么返回的也是聯(lián)合類型。

type Person = {
  age: number;
  name: string;
  alive: boolean;
};

// number|string
type T = Person['age'|'name'];

// number|string|boolean
type A = Person[keyof Person];

如果訪問不存在的屬性,會報錯。

type T = Person['notExisted']; // 報錯

方括號運(yùn)算符的參數(shù)也可以是屬性名的索引類型

type Obj = {
  [key:string]: number,
};

// number
type T = Obj[string];

這個語法對于數(shù)組也適用,可以使用number作為方括號的參數(shù)。

// MyArray 的類型是 { [key:number]: string }
const MyArray = ['a','b','c'];

// 等同于 (typeof MyArray)[number]
// 返回 string
type Person = typeof MyArray[number];

上面示例中,MyArray是一個數(shù)組,它的類型實際上是屬性名的數(shù)值索引,而typeof MyArray[number]typeof運(yùn)算優(yōu)先級高于方括號,所以返回的是所有數(shù)值鍵名的鍵值類型string。

方括號里面不能有值的運(yùn)算。

// 示例一
const key = 'age';
type Age = Person[key]; // 報錯

// 示例二
type Age = Person['a' + 'g' + 'e']; // 報錯

上面兩個示例,方括號里面都涉及值的運(yùn)算,編譯時不會進(jìn)行這種運(yùn)算,所以會報錯。

4. extends…?:條件運(yùn)算符

可以根據(jù)當(dāng)前類型是否符合某種條件,返回不同的類型。

T extends U ? X : Y

上面式子中的extends用來判斷,類型T是否可以賦值給類型U,即T是否為U的子類型,這里的TU可以是任意類型。如果T能夠賦值給類型U,表達(dá)式的結(jié)果為類型X,否則結(jié)果為類型Y。

// true
type T = 1 extends number ? true : false;

上面示例中,1number的子類型,所以返回true。

如果需要判斷的類型是一個聯(lián)合類型,那么條件運(yùn)算符會展開這個聯(lián)合類型。

(A|B) extends U ? X : Y

// 等同于

(A extends U ? X : Y) |
(B extends U ? X : Y)

上面示例中,A|B是一個聯(lián)合類型,進(jìn)行條件運(yùn)算時,相當(dāng)于AB分別進(jìn)行運(yùn)算符,返回結(jié)果組成一個聯(lián)合類型。

如果不希望聯(lián)合類型被條件運(yùn)算符展開,可以把extends兩側(cè)的操作數(shù)都放在方括號里面。

// 示例一
type ToArray<Type> =
  Type extends any ? Type[] : never;

// string[]|number[]
type T = ToArray<string|number>;

// 示例二
type ToArray<Type> =
  [Type] extends [any] ? Type[] : never;

// (string | number)[]
type T = ToArray<string|number>;

上面的示例一,傳入ToArray<Type>的類型參數(shù)是一個聯(lián)合類型,所以會被展開,返回的也是聯(lián)合類型。示例二是extends兩側(cè)的運(yùn)算數(shù)都放在方括號里面,所以傳入的聯(lián)合類型不會展開,返回的是一個數(shù)組。

條件運(yùn)算符還可以嵌套使用。

type LiteralTypeName<T> =
  T extends undefined ? "undefined" :
  T extends null ? "null" :
  T extends boolean ? "boolean" :
  T extends number ? "number" :
  T extends bigint ? "bigint" :
  T extends string ? "string" :
  never;

// "bigint"
type Result1 = LiteralTypeName<123n>;

// "string" | "number" | "boolean"
type Result2 = LiteralTypeName<true | 1 | 'a'>;

上面示例是一個多重判斷,返回一個字符串的值類型,對應(yīng)當(dāng)前類型。

5. infer關(guān)鍵字

用來定義泛型里面推斷出來的類型參數(shù),而不是外部傳入的類型參數(shù)。它通常跟條件運(yùn)算符一起使用,用在extends關(guān)鍵字后面的父類型中。

type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;

上面示例中,infer Item表示Item這個參數(shù)是 TypeScript 自己推斷出來的,不用顯式傳入,而Flatten<Type>則表示Type這個類型參數(shù)是外部傳入的。Type extends Array<infer Item>則表示,如果參數(shù)Type是一個數(shù)組,那么就將該數(shù)組的成員類型推斷為Item,即Item是從Type推斷出來的。

一旦使用Infer Item定義了Item,后面的代碼就可以直接調(diào)用Item了。下面是上例的泛型Flatten<Type>的用法。

// string
type Str = Flatten<string[]>;

// number
type Num = Flatten<number>;

上面示例中,第一個例子Flatten<string[]>傳入的類型參數(shù)是string[],可以推斷出Item的類型是string,所以返回的是string。第二個例子Flatten<number>傳入的類型參數(shù)是number,它不是數(shù)組,所以直接返回自身。

如果不用infer定義類型參數(shù),那么就要傳入兩個類型參數(shù)。

type Flatten<Type, Item> =
  Type extends Array<Item> ? Item : Type;

上面是不使用infer的寫法,每次調(diào)用Flatten的時候,都要傳入兩個參數(shù),就比較麻煩。

下面的例子使用infer,推斷函數(shù)的參數(shù)類型和返回值類型。

type ReturnPromise<T> =
  T extends (...args: infer A) => infer R 
  ? (...args: A) => Promise<R> 
  : T;

上面示例中,如果T是函數(shù),就返回這個函數(shù)的 Promise 版本,否則原樣返回。infer A表示該函數(shù)的參數(shù)類型為A,infer R表示該函數(shù)的返回值類型為R。

如果不使用infer,就不得不把ReturnPromise<T>寫成ReturnPromise<T, A, R>,這樣就很麻煩,相當(dāng)于開發(fā)者必須人肉推斷編譯器可以完成的工作。

下面是infer提取對象指定屬性的例子。

type MyType<T> =
  T extends {
    a: infer M,
    b: infer N
  } ? [M, N] : never;

// 用法示例
type T = MyType<{ a: string; b: number }>;
// [string, number]

上面示例中,infer提取了參數(shù)對象的屬性a和屬性b的類型。

下面是infer通過正則匹配提取類型參數(shù)的例子。

type Str = 'foo-bar';
type Bar = Str extends `foo-${infer rest}` ? rest : never // 'bar'

上面示例中,rest是從模板字符串提取的類型參數(shù)。

6. is運(yùn)算符

函數(shù)返回布爾值時,可以使用is運(yùn)算符,來限定返回值與參數(shù)之間的關(guān)系。

is運(yùn)算符用來描述返回值是true還是false。

function isFish(
  pet: Fish|Bird
):pet is Fish {
  return (pet as Fish).swim !== undefined;
}

上面示例中,函數(shù)isFish()的返回值類型為pet is Fish,表示如果參數(shù)pet類型為Fish,則返回true,否則返回false。

is運(yùn)算符總是用于描述函數(shù)的返回值類型,寫法采用parameterName is Type的形式,即左側(cè)為當(dāng)前函數(shù)的參數(shù)名,右側(cè)為某一種類型。它返回一個布爾值,表示左側(cè)參數(shù)是否屬于右側(cè)的類型。

is運(yùn)算符可以用于類型保護(hù)。

function isCat(a:any): a is Cat {
  return a.name === 'kitty';
}
let x:Cat|Dog;
if (isCat(x)) {
  x.meow(); // 正確,因為 x 肯定是 Cat 類型
}

上面示例中,函數(shù)isCat()的返回類型是a is Cat,它是一個布爾值。后面的if語句就用這個返回值進(jìn)行判斷,從而起到類型保護(hù)的作用,確保x是 Cat 類型,從而x.meow()不會報錯(假定Cat類型擁有meow()方法)

is運(yùn)算符還有一種特殊用法,就是用在類(class)的內(nèi)部,描述類的方法的返回值。

class Teacher {
  isStudent():this is Student {
    return false;
  }
}

class Student {
  isStudent():this is Student {
    return true;
  }
}

上面示例中,isStudent()方法的返回值類型,取決于該方法內(nèi)部的this是否為Student對象。如果是的,就返回布爾值true,否則返回false。

注意,this is T這種寫法,只能用來描述方法的返回值類型,而不能用來描述屬性的類型。

7. 模板字符串

ts可以使用模板字符串構(gòu)建類型,模板字符串最大的特點(diǎn)就是內(nèi)部可以引用其他類型。

type World = "world";

// "hello world"
type Greeting = `hello ${World}`;

上面示例中,類型Greeting是一個模板字符串,里面引用了另一個字符串類型world,因此Greeting實際上是字符串hello world

模板字符串可以引用的類型一共6種,分別是 string、number、bigint、boolean、null、undefined。引用這6種以外的類型會報錯。

模板字符串里面引用的類型,如果是一個聯(lián)合類型,那么它返回的也是一個聯(lián)合類型,即模板字符串可以展開聯(lián)合類型。

type T = 'A'|'B';

// "A_id"|"B_id"
type U = `${T}_id`;

上面示例中,類型U是一個模板字符串,里面引用了一個聯(lián)合類型T,導(dǎo)致最后得到的也是一個聯(lián)合類型。

如果模板字符串引用兩個聯(lián)合類型,它會交叉展開這兩個類型。

type T = 'A'|'B';

type U = '1'|'2';

// 'A1'|'A2'|'B1'|'B2'
type V = `${T}${U}`;

上面示例中,TU都是聯(lián)合類型,各自有兩個成員,模板字符串里面引用了這兩個類型,最后得到的就是一個4個成員的聯(lián)合類型。

8. satisfies運(yùn)算符

satisfies 是 TypeScript 4.9 版本中引入的一個新的運(yùn)算符,它可以讓你檢查一個給定的類型是否滿足一個特定的接口或條件。換句話說,它可以確保一個類型具有一個特定接口所要求的所有屬性和方法。它是一種保證一個變量符合一個類型定義的方式。

satisfies 運(yùn)算符的語法是在一個值后面加上 satisfies,然后跟上一個類型的名稱:

someValue satisfies SomeType;

satisfies 運(yùn)算符有以下優(yōu)點(diǎn):

  • 它可以讓你在不改變值的原始類型的情況下,對值的類型進(jìn)行驗證和約束(與 : 注解不同)。
  • 它可以讓你保留值的最具體的類型信息,而不是將其擴(kuò)展為更一般的類型(與默認(rèn)類型推斷不同)。

舉個例子,假設(shè)我們有一個 Vibe 接口,它定義了一個 mood 屬性,其類型為 "happy" | "sad"。我們可以用 satisfies 運(yùn)算符來保證我們創(chuàng)建的 vibe 對象的 mood 屬性只能是這兩個字符串字面量之一,同時還能保持 mood 屬性的具體值為 "happy"。

interface Vibe {
mood: "happy" | "sad";
}

const vibe = { mood: "happy" } satisfies Vibe;

vibe.mood; // "happy"

到此這篇關(guān)于TypeScript中的類型運(yùn)算符實現(xiàn)的文章就介紹到這了,更多相關(guān)TypeScrip 類型運(yùn)算符內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論