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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM