TS 類型表達中經常使用的關鍵字

TS 的類型系統爲咱們的代碼提供健壯性,可維護性的保障。javascript

固然除去一些基本類型外,咱們有時也須要表達一些複雜的類型,那這個時候靈活使用一些關鍵字就很方便了。html

這篇文章,就拿出一些能夠方便咱們進行類型表達的關鍵字來看一看。java

extends

extends 在類型表達時,有下面兩種用法:git

  1. 用於類型的繼承
interface Person {
    name: string;
    age: number;
}

interface Player extends Person {
    item: 'ball' | 'swing';
}
複製代碼
  1. 判斷是不是能賦值給另外一個類型
// 若是 T 能夠知足類型 Person 則返回 Person 類型,不然爲 T 類型
type IsPerson<T> = T extends Person ? Person : T;
複製代碼

typeof

在 TS 中用於類型表達時,typeof 能夠用於從一個變量上獲取它的類型。github

舉一個沒有卵用的例子:typescript

const value: number = 10;

const value2: typeof vlaue = 100;       // const value2: number
複製代碼

可是請注意下面這種狀況:數組

const value = 10;

const value2: typeof vlaue = 100;       

// const value2: 10
// ERROR: Type '100' is not assignable to type '10' 
複製代碼

經測試,number string boolean 類型在沒有聲明而直接賦值的狀況下,都會存在這個問題markdown

舉個有點用的例子

對於對象,數組,函數類型來說,這個仍是有點用的。參考函數

const data = {
  value: 123,
  text: 'text',
  subData: {
    value: false
  }
};

type Data = typeof data;

// type Data = {
// value: number;
// text: string;
// subData: {
// value: boolean;
// };
// }
複製代碼

keyof

keyof 是TS中的索引類型查詢操做符。keyof T 會獲得由 T 上已知的公共屬性名組成的聯合類型。oop

Keyof T, the index type query operator. For any type T, keyof T is the union of known, public property names of T

舉個例子:

interface Person {
    name: string;
    age: number;
    phoneNum: number;
}

type PersonProperty = keyof Person;

// type PersonProperty = "name" | "age" | "phoneNum"
複製代碼

keyof 在咱們限制類型或者枚舉屬性時仍是很是常見的,好比下面這個小例子:

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}
複製代碼

這樣當咱們嘗試獲取不在目標對象上的屬性值時,TS會爲咱們檢查到這樣簡單的錯誤

T[K] 在TS裏稱做索引訪問操做符(indexed access operator)。它能夠爲咱們準確解析目標對象上的對應屬性的正確類型。

在下面的介紹,咱們能夠繼續看到 keyof 的應用。

in

in 操做符用於遍歷目標類型的公開屬性名。相似 for .. in 的機制。

從其用途看,咱們很容易想到 in 可用於聯合類型或者枚舉類型。

使用於枚舉類型

咱們能夠像下面這樣使用枚舉類型

enum Letter {
    A,
    B,
    C,
}

type LetterMap = {
    [key in Letter]: string;
}

// type LetterMap = {
// 0: string;
// 1: string;
// 2: string;
// }
複製代碼

使用於聯合類型

咱們能夠像下面這樣使用聯合類型

type Property = 'name' | 'age' | 'phoneNum';

type PropertyObject = {
    [key in Property]: string;
}

// type PropertyObject = {
// name: string;
// age: string;
// phoneNum: string;
// }
複製代碼

利用可用於聯合類型的特性,咱們有下面這種很常見的作法(僅舉例):

type ToString<T> {
    [key in keyof T]: string;
}
複製代碼

使用於基礎類型

值得一提的是,一些基礎類型(string, number, symbol)也能夠用於 in 操做符:

type StringKey = {
    [key in string]: any;
}

// type StringKey = {
// [x: string]: any;
// }

type NumberKey =  {
    [key in number]: any;
}

// type NumberKey = {
// [x: number]: any;
// }

type SymbolKey = {
    [key in symbol]: any;
}

// 這裏和預想的不同 TODO
// type SymbolKey = {}
複製代碼

關於這一點,我認爲和TS中,stringnumber 是惟二兩個能夠作 索引簽名 的類型是一致的

interface PersonArray {
    [index: number]: Person;
}

interface PlainObject {
    [key: string]: any;
}
複製代碼

infer

關於 infer 操做符,這個能夠用來進行類型推測。舉個簡單的小例子:

在 TS 中,若是咱們在 generator 函數中使用了 yield 表達式,咱們就會丟失類型。好比下面這樣:

function returnSomething() {
    // return something;
}

function* task() {
    // 這裏的 result 在TS中是沒有拿到正確的函數返回類型的,你們能夠試一下
    const result = yield returnSomething();
}
複製代碼

那爲了解決相似的問題,TS 爲咱們內置了 ReturnType 的映射類型:

function* task() {
    // 這裏的 result 在TS中是沒有拿到正確的函數返回類型的,你們能夠試一下
    const result: ReturnType<typeof returnSomething> = yield returnSomething();
}
複製代碼

那咱們來看一下 ReturnType 是如何作的呢,其實很簡單,就是用到了 infer:

type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
複製代碼

infer P 中的 P 便是表示待推斷的返回值類型。

關於 infer 的更多內容,你們能夠參考這篇文章,也但願你們能夠多多支持做者

is

is 操做符用於TS的類型謂詞中,是實現TS類型保護的一種方式(關於什麼是類型保護)。

好比下面這種場景:

function doSometing(value: string | number) {
    if (typeof value === 'string') {
        // TS 能夠識別這個分支中 value 是 string 類型的參數(這就叫類型保護)
        // do something
    } else {
        // TS 能夠識別這個分支中 value 是 number 類型的參數
        // do something
    }
}
複製代碼

除去上面這種方式之外,咱們可使用TS的類型謂詞來實現:

/** * 此函數用於判斷參數 value 是否是 string 類型 * * 因爲返回類型聲明瞭類型謂詞,能夠幫助TS在代碼分支中進行類型保護(默認返回 boolean 類型是沒辦法作到的) **/
function isString(value: any): value is string {
    return typeof value === 'string';
}

function doSometing(value: string | number) {
    if (isString(value)) {
        // TS 能夠識別這個分支中 value 是 string 類型的參數(這就叫類型保護)
    } else {
        // TS 能夠識別這個分支中 value 是 number 類型的參數
    }
}
複製代碼

這樣作的好處是:實現了代碼複用,實現了更好的語義化。

其實,TS 代碼中 Array.isArray 即是使用了這樣的聲明。

interface ArrayConstructor {
    // ...
    isArray(arg: any): arg is Array<any>;
}
複製代碼
相關文章
相關標籤/搜索