typescript不能不掌握的高級特性(二)

引言

上一篇我重點講述了 ts 的交叉類型,本期將結合實例重點講述 ts 中的一些高級操做符。本篇文章略長,筆者以前的文章都略短,做爲男人仍是要好好學習,文章仍是長點好。javascript

本期涉及的操做符以下:html

  • keyof
  • in
  • infer 關鍵字
  • Parameters
  • ReturnType
  • InstanceType
  • ConstructorParameters
  • ThisParameterType
  • OmitThisParameter

本篇文章適合有必定基礎的 ts 開發,若是你徹底沒有用過,請先到官網學習官方文檔java

經過上述操做符的學習,但願能達到如下效果:git

  • 再也不爲大佬寫的 ts 定義而苦惱了
  • 看源碼定義再也不吃力了
  • 本身的 ts 代碼更加智能,再也不是滿屏的 any 了。

下面我將結合具體實慄向你們講述 ts 中的高級操做符。github

keyof

定義

keyofObject.keys略有類似,只是 keyof 是取 interface 的鍵,並且 keyof 取到鍵後會保存爲聯合類型。typescript

interface iUserInfo {
  name: string;
  age: number;
}
type keys = keyof iUserInfo;
複製代碼

keyof 的簡單栗子

咱們有這樣一個需求,實現一個函數 getValue 取得對象的 value。在未接觸 keyof 時,咱們通常會這樣寫:數組

function getValue(o: object, key: string) {
  return o[key];
}
const obj1 = { name: '張三', age: 18 };
const name = getValue(obj1, 'name');
複製代碼

可是,這樣寫就喪失了 ts 的優點:bash

  • 沒法肯定返回值類型
  • 沒法對 key 進行約束,可能會犯拼寫的錯誤

這時咱們可使用 keyof 來加強 getValue 函數的類型功能。async

使用 keyof 後咱們能夠看到,能夠完整的提示能夠輸入的值,當拼寫錯誤時也會有清晰的提示。

function getValue<T extends Object, K extends keyof T>(o: T, key: K): T[K] {
  return o[key];
}

const obj1 = { name: '張三', age: 18 };
const a = getValue(obj1, 'hh');
複製代碼

in

in用於取聯合類型的值。主要用於數組和對象的構造。函數

type name = 'firstName' | 'lastName';
type TName = {
  [key in name]: string;
};
複製代碼

const data1 = [
  {
    a1: 'a',
    b1: 'b',
    c1: 'c',
    d1: 'd',
  },
];

const data2 = [
  {
    a2: 'a',
    b2: 'b',
  },
];
複製代碼

但切記不要用於 interface,不然會出錯

infer

先看官方解釋:

Within the extends clause of a conditional type, it is now possible to have infer declarations that introduce a type variable to be inferred. Such inferred type variables may be referenced in the true branch of the conditional type. It is possible to have multiple infer locations for the same type variable.

翻譯過來就是:

如今在有條件類型的 extends 子語句中,容許出現 infer 聲明,它會引入一個待推斷的類型變量。 這個推斷的類型變量能夠在有條件類型的 true 分支中被引用。 容許出現多個同類型變量的 infer。

初步看來,這個 ts 關鍵字限制比較多,也是筆者以爲比較難理解的,可是它對咱們獲取一些比較複雜的類型特別有用。使用過程當中須要注意如下幾個關鍵點

  • 只能出如今有條件類型的 extends 子語句中;
  • 出現 infer 聲明,會引入一個待推斷的類型變量;
  • 推斷的類型變量能夠在有條件類型的 true 分支中被引用;
  • 容許出現多個同類型變量的 infer

要完全理解這個關鍵詞的使用必須結合一些實例。

infer 實例

使用 infer 獲取函數參數 Parameters

好比咱們這裏定義了一個函數類型 TArea,如今要實現將函數的參數類型取出來,咱們該怎麼作呢?

type TArea = (width: number, height: number) => number;
type params = Parameters<TArea>;
複製代碼

其實 Parameters 方法 ts 已內置,源碼以下:

type Parameters<T extends (...args: any) => any> = T extends (
  ...args: infer P
) => any
  ? P
  : never;
複製代碼

咱們仔細研讀一下以上源碼,發現遵循咱們上面所說的 infer 知足的四個特色:

  • 只能出如今有條件類型的 extends 子語句中;
  • 出現 infer 聲明,會引入一個待推斷的類型變量;
  • 推斷的類型變量能夠在有條件類型的 true 分支中被引用;
  • 容許出現多個同類型變量的 infer

這裏再囉嗦幾句,由於咱們要獲取函數參數,因此傳遞的參數必須是個函數,因此有 T extends (...args: any) => any,因爲咱們要獲取的是函數參數的類型,因此 infer 出如今了函數參數位置。

同理獲取函數返回值的方法就呼之欲出了,若是仍是寫不出來,當我沒說。

使用 infer 獲取函數返回值 ReturnType

ReturnType 方法 ts 已內置

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

再看一下圖,不要說我騙你!

了不起了,infer 真是太強大了,下面咱們繼續看 infer 如何獲取一個類實例的類型。

獲取實例類型 InstanceType

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

偷偷告訴你,聰明的 ts 官方也內置了這個工具。

獲取構造函數類型 ConstructorParameters

該方法 ts 已內置咱們看一下源碼

type ConstructorParameters<
  T extends new (...args: any) => any
> = T extends new (...args: infer P) => any ? P : never;
複製代碼

咱們能夠這樣使用它

獲取參數 this 參數 ThisParameterType

type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any
  ? U
  : unknown;
複製代碼

剔除 this 參數 OmitThisParameter

實現效果以下,你們能夠本身手動實現一下,這能夠很好的訓練一下 infer 的使用。

官方源碼以下:

type OmitThisParameter<T> = unknown extends ThisParameterType<T>
  ? T
  : T extends (...args: infer A) => infer R
  ? (...args: A) => R
  : T;
複製代碼

咱們能夠這樣理解:若是傳遞的函數不包含 this 參數,則直接返回。如下語法用於判斷是否包含 this 參數

unknown extends ThisParameterType<T>
複製代碼

總結

咱們重點講述了 ts 中 keyof 和 infer 的高級用法,下面以兩個思考題結束本篇文章,具體答案會在下篇文章揭曉。

思考題 1

這是一道 leetcode 的 ts筆試題,原題目略長,就不直接貼出來了,這裏簡化一下:

// 假設有一個這樣的類型:
interface initInterface {
  count: number;
  message: string;
  asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>;
  syncMethod<T, U>(action: Action<T>): Action<U>;
}
// 在通過 Connect 函數以後,返回值類型爲

type Result {
  asyncMethod<T, U>(input: T): Action<U>;
  syncMethod<T, U>(action: T): Action<U>;
}
// 其中 Action<T> 的定義爲:
interface Action<T> {
  payload?: T
  type: string
}
// 如今要求寫出Connect的函數類型定義。
複製代碼

思考題二

// 有原數組以下
const data1 = [
  {
    a1: 'a',
    b1: 'b',
    c1: 'c'
  }
];
// 實現一個函數 transformData ,傳遞一個keyMap後,結果返回通過keyMap轉換後的數組

const A2 = transformData(data1, { a1: 'a2' }); // 返回 [{a2: 'a'}]
const A2 = transformData(data1, { a1: 'a2',b2: 'b1' }); // 返回 [{a2: 'a', b2: 'b']

// 要求用ts完成,必須有完善類型推斷,不能出現any
複製代碼

@Author: WaterMan

相關文章
相關標籤/搜索