typescript 中的keyof、 in

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

本期涉及的操作符如下:

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

本篇文章適合有一定基礎的 ts 開發,如果你完全沒有用過,請先到官網學習官方文檔

 

通過上述操作符的學習,希望能達到以下效果:

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

下面我將結合具體實慄向大家講述 ts 中的高級操作符。

keyof

定義

keyof與Object.keys略有相似,只是 keyof 是取 interface 的鍵,而且 keyof 取到鍵後會保存爲聯合類型。

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 的優勢:

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

這時我們可以使用 keyof 來增強 getValue 函數的類型功能。

 

使用 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 的高級用法,下面以兩個思考題結束本篇文章,具體答案會在下篇文章揭曉。

 

轉載: https://juejin.cn/post/6844904145732763655#heading-0

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