React中的這個TS騷操作秀到我了

Sunshine_Lin發表於2022-01-29
來源:https://juejin.cn/post/705294...
作者:Angus安格斯

前言

最近在寫React時定義變數型別時,無意點到了ElementType,然後在node_modules/@types/react/index.d.ts 中發現了這段程式碼:

image.png

牛啊,雖然我沒太看懂,但是就是覺得挺牛的。

Google一下

但是後來我們一Google,發現網上管這個叫型別過濾,是一個實用的TS寫法。\
來看下面這個?:

 interface Example{
     a:string;
     b:number;
     c:number;
     d:string;
     ...
     
 }
複製程式碼

我們有一個Example的介面,但是我們們現在想對這個介面做一些處理,只希望留下型別為string的屬性,這樣我們們應該咋做?就可以使用我們們自己定義的FilterType。

    type newExample = FilterType<Example,string> // {a:string;d:string;...}
複製程式碼

我們們來看看FilterType是如何實現的:

    type FilterType<Source, Types> = Pick<
      Source, 
      {
        [K in keyof Source]: Source[K] extends Types ? K : never
      }[keyof Source]
    >;
複製程式碼

分析

首先我們們先來看看程式碼中出現的幾個utility types。(大佬直接跳過)

in

in 可以遍歷列舉型別,類似 for...in

type Example = 'a' | 'b' | 'c' | 'd'
type Obj = {
  [T in Example]: string;  // 遍歷Example,把每個key都賦值string型別
}
/* 等價於 
  type Obj = {
    a: string;
    b: string;
    c: string;
    d: string;
  }
*/
複製程式碼

keyof

TS官方文件中把它稱作索引型別查詢操作符,其實就是取到型別的key,類似Object.keys() 雖然我不知道這樣類比對不對。

interface Example {
    a: string;
    b: string;
    c: number;
    d: boolean;
}
    
type Keys = keyof Example   // 等價於 type Keys = 'a' | 'b' | 'c' | 'd'
複製程式碼

條件判斷

interface A {
    a:string;
}

interface B extends A {
    b:string;
} 

// B是否繼承於A?若是,則為number型別;若不是,則為string型別
type C = B extends A ? number : string  // 等價於 type C = number
複製程式碼

開始剖析

先看一個?,揭開她的面紗,不,是口罩:

type Mask<source,types> = {
    [K in keyof source]:source[K] extends types ? K : never;
}
interface Example{
    name:string;
    height:number
}
type newType = Mask<Example,string> // { name:'name'; height:never}
複製程式碼

這時候我們們看到一個{ name:'name'; height:never} ,what??? 這有啥用?

別急!接下來我們們繼續看下面這個例子,揭開她的外套:\
來看一個可以記在筆記本上的小知識點:索引訪問介面屬性

type person = { 
    name:"Angus";
    height:185;
}["name" | "height"]
複製程式碼

等價於:

type person = "Angus" | 185
複製程式碼

注意的是,當value為never時,就會訪問不到

type person = { 
    name:"Angus";
    height:185;
    girlFriend:never;
}["name" | "height" | "girlFriend"]
複製程式碼

等價於

type person = "Angus" | 185
複製程式碼

(singleDog的自嘲

那知道了索引訪問介面屬性有啥用呢?來看下面這個?,揭開她的clothes:

type Clothes<source,types> = {
    [K in keyof source]:source[K] extends types ? K : never;
}[keyof source]

interface Example{
    name:string;
    height:number;
    home:string
}
type newType = Clothes<Example,string> // type newType = "name | home"
複製程式碼

這樣我們就能拿到我們想要的型別的聯合型別。\
最後一步,我們們揭開*\
hold on ,我們們先了解一下Pick。\
Pick是在一個型別物件中,挑選幾個型別,組成一個新的的型別。

interface Angus{
    name:string;
    height:number;
    wight:number;
}
type newAngus = Pick<Angus,'name' | 'height'> //{name:string;height:number}
複製程式碼

它的實現:

    type Pick<T,K extends keyof T> = {
        [P in K] : T[P];
    }
複製程式碼

然後我們來看Google上的那個?:

type FilterType<Source, Types> = Pick<
      Source, 
      {
        [K in keyof Source]: Source[K] extends Types ? K : never
      }[keyof Source]
    >;
複製程式碼

是不是茅廁頓開!!!通過Pick就可以挑選我們想要的屬性了。

    {
       [K in keyof Source]: Source[K] extends Types ? K : never
     }[keyof Source]
複製程式碼

這兩行的程式碼就能拿到我們想要的屬性。再通過Pick一下就完事。有沒有被秀到?

擴充

那我們們再來看看TS中其他的一些工具型別吧

Omit

Omit(a,b) 接收兩個引數,第一個是要編輯的基本型別,第二個是你要刪除的型別。

    type Person = {
        name:string;
        sex:string;
    }
    type newPerson = Omit<Person,'name'> // {sex:string}
複製程式碼

看看它咋實現的:

    type Omit <T,K extends keyof any> = Pick<T,Exclude<keyof T,K>>;
複製程式碼

Partial

Partial 可以快速把某個介面型別中定義的屬性變成可選的(Optional)

    type Person = {
        name:string;
        sex:string;
    }
    type newPerson = Partial<Person> // {name?:string;sex?:string}
複製程式碼

看看它咋實現的:\

    type Partial <T> = {
        [P in keyof T]?: T[P]
    }
複製程式碼

Exclude

用於刪除型別集合中的指定型別.

    type a = string | number
    type newPerson = Exclude<a,string>  //number
複製程式碼

實現:

    type Exclude<T, U> = T extends U ? never : T
複製程式碼

Readonly

將介面所有屬性變為只讀的.

    type Person = {
        name:string;
        sex:string;
    }
    type newPerson = Readonly<Person> 
    // type newPerson = {readonly name: string;readonly sex: string;}
複製程式碼

實現:

    type Readonly<T> = { readonly [P in keyof T]: T[P]; }
複製程式碼

ReturnType

ReturnType的作用是用於獲取函式 T 的返回型別。\

    type T0 = ReturnType<() => string>; // string 
    type T1 = ReturnType<(s: string) => void>; // void
    type T2 = ReturnType<() => T>; // {} 
    type T3 = ReturnType<() => T>; // number[] 
    type T4 = ReturnType; // any 
    type T5 = ReturnType; // any 
    type T6 = ReturnType; // Error 
    type T7 = ReturnType; // Error
複製程式碼

實現:

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

Parameters

Parameters的作用是用於獲取函式 T 的引數型別。

type getuserInfo = (user: string) => void
// Parameters<T>的作用是用於獲取函式 T 的引數型別。
type ParametersUserInfo = Parameters<getuserInfo>
複製程式碼

就先介紹到這裡啦,目前大四,明年就要去網易搬磚啦,碼字不易,隨手點個讚唄,感謝大佬。

結語

我是林三心,一個熱心的前端菜鳥程式設計師。如果你上進,喜歡前端,想學習前端,那我們們可以交朋友,一起摸魚哈哈,摸魚群,加我請備註【思否】

image.png

相關文章