TS06 TypeScript高級用法

使用泛型+type定義類型函數

使用type+泛型,可以定義類型函數:

type foo<T> = T;
type bar = foo<string>

可以對泛型入參進行約束、設置默認值

// 對入參進行約束
type foo<T extends string> = T;
type bar = foo<string>

// 設定默認值
type foo<T extends string = '123'> = T;
type bar = foo<string>

條件判斷

TypeScript中使用extends進行類型判斷,與三元運算符很相似:

T extends U ? X : Y;

如果T的類型能夠extends U,結果返回X,否則返回Y

結合上面的類型函數,可以進行擴展:

type num = 1;
type str = 'hello world';

type IsNumber<N> = N extends number ? 'yes is a number' : 'no not a number';

type result1 = IsNumber<num>; // 類型爲 'yes is a number'
type result2 = IsNumber<str> // 類型爲  'no not a number'

遍歷聯合類型

使用in關鍵在來遍歷type的聯合類型

聯合類型就是使用|來定義的類型的合集

type Key = 'vue' | 'react';

type Mapped = {[k in Key]: string}; // {vue: string; react: string}

const bar: Mapped = {
  vue: 's',
  react: 's'
}

如果聯合類型不是我們顯式的定義出來的,那麼想要動態的推導出聯合類型的類型,需要使用keyof方法

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

type Foo = keyof Person; // 'name' | 'age'

對聯合類型進行map操作

可以使用extends+泛型,實現將一組聯合類型批量映射爲另一種類型:

type Foo = 'a' | 'b';
type UnionTypesMap<T, U> = U;

type Bar = UnionTypesMap<Foo, 5>

全局作用域

使用declare關鍵字來聲明全局作用域:

declare module '*.png';
declare module '*.svg';
declare module '*.jpg';

declare type str = string;
declare interface Foo {
  propA: string;
  propB: number;
}

要注意,如果模塊使用了export關鍵字導出了內容,上述方式可能會失效,需要顯式的聲明到全局:

declare global {
  const ModuleGlobalFoo: string;
}

注意,上述方式之恩能夠用在模塊聲明內,也就是說代碼中必須包含export,否則就會報錯:

TS2669: Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.

模塊作用域

模塊作用域的觸發條件之一就是使用export關鍵字導出內容,在其他模塊獲取時需要import導入

never類型的作用

never類型代表空集,常用語校驗“類型收窄”是否符合預期,常被用來做“類型收底”。

例如,有一個聯合類型時:

interface Foo {
  type: 'foo'
}

interface Bar {
  type: 'bar'
}

type All = Foo | Bar

switch判斷type,TS是可以收窄類型的(discriminated union):

funcfunction handleValue(val: All) {
  switch (val.type) {
    case 'foo':
      // 這裏 val 被收窄爲 Foo
      break
    case 'bar':
      // val 在這裏是 Bar
      break
    default:
      // val 在這裏是 never
      const exhaustiveCheck: never = val
      break
  }
}

default裏面把收窄的neverval賦給了顯示聲明爲never的變量。如果有一天type類型被修改了:

type All = Foo | Bar | Baz

如果沒有在handleValue添加針對Baz的處理邏輯,這時候在default的分支中就會編譯錯誤。所以通過這個辦法可以確保switch總是窮盡了所有All的可能性

never進行類型過濾聯合類型

never參與運算時T | never的結果是T,根據這個規則就可以過濾聯合類型中不符合期望的類型,TS內置的Exclude泛型操作符就是根據這個原理實現的:

/**
 * Exclude from T those types that are assignable to U
 */
type Exclude<T, U> = T extends U ? never : T;

type A = 1 | 2;
type B = Exclude<A, 1>; // 2

自定義類型守衛

類型守衛(Type Guard)的目的就是爲了對類型進行分流,將聯合類型分發到不同管道。可以出發類型守衛的常見方式有:typeofinstancofin=====!=!==

function foo(x: A | B) {
  if (x instanceof A) {
    // x is A
  } else {
    // x is B
  }
}

當以上的方式不滿足需求時,可以通過is關鍵字自定義類型守衛:

function isA(x): x is number {
  return true
}

function foo(x: unknown) {
  if(isA(x)) {
    return x
  }
  return null;
}

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章