TypeScript工具類型

類型挑戰(zhàn)【轉載】:https://wangtunan.github.io/blog/typescript/challenge.html#%E4%BB%8B%E7%BB%8D

工具類型

Partial<Type>(可選)

  • 解釋:用來構造 (創(chuàng)建) 一個類型福澡, T 的所有屬性設置為可選的

    interface User {
      id: number;
      name: string;
    }
    const obj1: Partial<User> = { id: 25 }  // 正確
    
  • 實現(xiàn):type Partial<T> = { [P in keyof T]?: T[P]; }

    • [P in keyof T] 通過映射類型,遍歷T上的所有屬性
    • ?: 語法稱為映射修飾符稚机,用于影響可選性牍戚。
  • 常用場景一:構造一個新類型

    interface Person {
      name: string;
      age: number;
    }
    type PartialPerson = Partial<Person>
    
  • 常用場景二:獲取由對象屬性組成的類型

    const obj = {
      id: 1,
      name: 'James',
      salary: 100,
    }
    type Test = Partial<typeof obj>;
    

    必須使用 typeof 類型運算符侮繁,因為 Partial 需要一個類型

映射修飾符可以以兩種方式影響可選性,可以通過前綴 - 或者 + (即-?:/+?:)刪除或者添加這些修飾符如孝,如果沒有寫前綴宪哩,相當于使用了 + 前綴

Required<Type>(必選)

  • 解釋:聲明類型中的每一項都是必需項

    interface User {
      id?: number;
      name?: string;
    }
    
    const obj1: Required<User> = { id: 25 };    // Error 缺少屬性
    const obj2: Required<User> = { id: 25, name: '666' }; // 正確
    
  • 實現(xiàn):type Required<T> = { [P in keyof T]-?: T[P]; }

    • 這里的 -? 就是抵消掉問號 ?

Readonly<Type>(只讀)

  • 解釋:將 Type 的所有屬性都設置為 readonly (只讀 ), 構造出來的結構完全相同,但所有屬性為只讀

    interface User {
      des: string;
    }
    
    // type ReadonlyUser = Readonly<User>
    const todo: Readonly<User> = {
      des: "A student",
    };
    
    todo.des = "Hello"; // Error 類型值不可再賦值改變
    
  • 內部實現(xiàn):type Readonly<T> = { readonly [P in keyof T]: T[P]; }

    • 主要實現(xiàn)是通過映射遍歷所有key第晰,然后給每個key增加一個readonly修飾符

Record<Keys, Type>

  • 構造一個對象類型锁孟,其屬性鍵為Keys彬祖,屬性值為Type。此實用程序可用于將一個類型的屬性映射到另一個類型品抽。

    interface PageInfo {
      id: number;
      title: string;
      isCache: Boolean
    }
    type Page = "home" | "about" | "login";
    
    type NavType = Record<Page, PageInfo>
    /**
     * =>
     * type NavType ={
     *  home: PageInfo;
     *  about: PageInfo;
     *  login: PageInfo;
     * }
     */
    
    const nav: NavType = {
      home: { id: 1, title: 'home Page', isCache: false },
      about: { id: 1, title: 'home Page', isCache: false },
      login: { id: 1, title: 'home Page', isCache: false },
    }
    
  • 實現(xiàn):type Record<K extends keyof any, T> = { [P in K]: T; }

    • 核心實現(xiàn)就是遍歷K储笑,將值設置為T;

    • 注意的是keyof any得到的是string | number | symbol,原因在于類型key的類型只能為string | number | symbol

Pick<Type, Keys>(挑選)

  • Type 中選取一組屬性 Keys(字符串字面值或字符串字面值的并集)來構造一個新類型圆恤。

    • 即:獲取一個類型中的某些key
    interface User {
      name: string;
      age: number;
      address: string;
    }
    type PickUser = Pick<User, 'age' | 'name'>;
    /**
     * =>
     * type PickUser = {    
     *  age: number;    
     *  name: string;
     * }
     */
    
  • 實現(xiàn):type Pick<T, K extends keyof T> = { [P in K]: T[P]; }

    • extends 限制了 K 的值必須屬于 Type 的屬性值(keyof Type)

Omit<Type, Keys>(省略)

  • 通過從Type選取所有屬性然后移除Keys(字符串字面值或字符串字面值的并集)來構造類型突倍。

    • 即:移除一個類型的某些key
    interface User {
      name: string;
      age: number;
      address: string;
    }
    type OmitUser = Omit<User, 'address'>;
    /**
     * =>
     * type OmitUser = {
     *  age: number;
     *  name: string;
     * }
     */
    
  • 1.利用Pick實現(xiàn):type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>

    • 從聯(lián)合類型T中排除K后,剩下的屬性構成一個新的類型盆昙,即‘我們需要的屬性聯(lián)合’

    • 利用Pick提取需要的Keys組成的類型:Omit = Pick<T, 我們需要的屬性聯(lián)合>

  • 2.利用映射類型實現(xiàn):type Omit<T, K extends keyof any> = { [P in Exclude<keyof T, K>]: T[P]; }

    • 類似Pick羽历,通過映射類型遍歷Exclude<keyof T, K>的屬性
    • T[P] 設置類型為原來的類型

Exclude<T, U> (排除)

  • T中剔除可以賦值給U的類型。(兩者的差集

  • 實現(xiàn):type Exclude<T, U> = T extends U ? never : T

  • 遍歷 T 中的所有子類型淡喜,如果該子類型約束于約束于U(存在于U秕磷、兼容于U),則返回never炼团,否則返回該子類型

  • never表示一個不存在的類型澎嚣,與其它類型聯(lián)合后是沒有never

type T0 = Exclude<"a" | "b" | "c", "a">;
/**
* => type T0 = "b" | "c"
*/

Extract<T, U>(提取)

  • 提取T中可以賦值給U的類型瘟芝。(兩者的交集)

    type Test1 = Extract<"a" | "b" | "c", "a" | "f">;
    // => type Test1 = "a"
    
  • 實現(xiàn):type Extract<T, U> = T extends U ? T : never;

    • 遍歷T币叹,T的子類型存在于U,則返回該子類型模狭,否則返回never

NonNullable<Type>(非空)

  • 通過從 Type 中排除 nullundefined 來構造一個類型颈抚。

  • 實現(xiàn):type NonNullable<T> = T & {};

    type T0 = NonNullable<string | number | undefined | null>;
    // => type T0 = string | number
    

Parameters<Type>(參數的類型)

  • 返回由函數參數類型組成的元組類型

  • 實現(xiàn):type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

    • 首先約束參數T必須是一個函數類型,所以(...args: any) => any替換成Function也是可以的嚼鹉;

    • 具體實現(xiàn):判斷T是否是個函數類型贩汉,如果是則使用infer PTS自己推導出函數的參數類型,并將推導結果存到類型P上锚赤,否則就返回never

    • 示例:

    type T0 = Parameters<() => string>;
    // => type T0 = []
    
    type T1 = Parameters<(s: string) => void>;
    // => type T1 = [s: string]
    
    type T2 = Parameters<<T>(arg: T) => T>;
    // => type T2 = [arg: unknown]
    
    declare function f1(arg: { a: number; b: string }): void;
    type T3 = Parameters<typeof f1>;
    // => 
    // type T3 = [arg: {
    //     a: number;
    //     b: string;
    // }]
    
    type Eg = Parameters<(arg1: string, arg2: number) => void>;
    // => type Eg = [arg1: string, arg2: number]  // 這是一個元組
    
    function sum(a: number, b: number): number {
      return a + b;
    }
    type SumParamsType = Parameters<typeof sum>;
    // => type SumParamsType = [a: number, b: number]
    

擴展infer

  • 關鍵詞infer的作用是讓TS自己推導類型匹舞,并將推導結果存儲在其參數綁定的類型上。

  • infer只能在extends條件類型上使用线脚,不能在其它地方使用赐稽。

  • 擴展: infer實現(xiàn)一個推導數組所有元素的類型:

    /**
     * 約束參數T為數組類型,
     * 判斷T是否為數組浑侥,如果是數組類型則推導數組元素的類型
     */
    type FalttenArray<T extends Array<any>> = T extends Array<infer P> ? P : never;
    
    type Eg1 = FalttenArray<[number, string]>
    // => type Eg1 = number | string;
    
    type Eg2 = FalttenArray<[1, 'asd']>
    // => type Eg2 = 1 | "asd"
    

ReturnType<Type>(返回值類型)

  • 獲取函數的返回值類型

  • 實現(xiàn):type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any

    • Parameters<Type>相比姊舵,ReturnType<Type>只是將infer R從參數位置移到返回值位置,此時R表示待推斷的返回值類型
  • 示例:

    type Func = () => { a: number; b: string };
    type Test = ReturnType<Func>;
    // =>
    // type Test = {
    //   a: number;
    //   b: string;
    // }
    
    type T0 = ReturnType<() => string>;         // type T0 = string
    type T1 = ReturnType<(s: string) => void>;  // type T1 = void
    type T2 = ReturnType<<T>() => T>;           // type T2 = unknown
    
    type T3 = ReturnType<<T extends U, U extends number[]>() => T>;
    // type T3 = number[]
    

    非法的例子:均不滿足(...args: any): any寓落,type T 將被視為any處理史飞。

    type T = ReturnType<string>;    // Error
    type T = ReturnType<Function>;  // Error
    

ConstructorParameters<Type>(獲取構造函數參數類型)

  • 可以獲取類的構造函數的參數類型构资,存在一個元組中拦赠。(如果type不是函數允乐,則為never類型)鳞陨。

  • 實現(xiàn):type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never

    • 核心還是用infer推導構造函數的參數類型

    • 首先約束參數T是擁有構造函數的類掏导,判斷T是滿足約束的類時值纱,利用infer P自動推導構造函數的參數類型妈橄,并最終返回該類型沙热。

    • new (...args: any)是構造簽名,new (...args: any) => any是構造函數類型字面量

  • 示例:

    type Test = ConstructorParameters<(new (name: string) => any) | (new (age: number) => any)>;
    // => type Test = [name: string] | [age: number]
    
    class Person {
      name: string;
      age: number;
    
      constructor( name: string, age: number) {
        this.name = name;
        this.age = age;
      }
    }
    type PersonParamsType = ConstructorParameters<typeof Person>
    // => type PersonParamsType = [name: string, age: number]
    

擴展1:為什么要對T約束為抽象類(abstract

abstract用來定義抽象類以及抽象類中的抽象方法

// 普通類
class Test {}
// 抽象類
abstract class TestAbst {}

const T1: typeof Test = Test      // 可以賦值
const T2: typeof Test = TestAbst  // Error: 無法將 抽象構造函數類型 分配給 非抽象構造函數類型。

const TAbs1: typeof TestAbst = Test      // 可以賦值
const TAbs2: typeof TestAbst = TestAbst  // 可以賦值
  • 可以將抽象類(抽象構造函數)賦值給抽象類或者普通類,反之不行唁情。

擴展2:關于類類型的表示

如上const T1: typeof Test = Test,雖然用typeof表示類的類型非常方便(其實它不是用來干這個的, 至少不全是),但不能因每次要寫一個類類型時都先用class關鍵詞定義一個類。

替代方法:使用構造函數表示class的類型

type PClass = new (name: string, age: number) => { name: string, age: number };

class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

const TestPerson: PClass = Person

也可以在接口interface中使用:

interface PInter {
  new (name: string, age: number): { name: string, age: number }
}

const TestPerson2: PInter = Person

InstanceType<Type>(獲取構造函數返回值的類型)

InstanceType用于獲取類的實例化類型鳍贾,即 new 類 的產物,可以說是執(zhí)行構造函數的返回值,與上面的ConstructorParameters類型相對應

  • 實現(xiàn):
    type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;

    • ConstructorParameters<Type>相比瓮顽,只需要對infer的使用換了個位置暖混。
    class Person {
      constructor(name?: string, age?: number) { }
    }
    
    const T: InstanceType<typeof Person> = new Person()
    
    type T1 = InstanceType<typeof Person>
    type T2 = InstanceType<any>
    type T3 = InstanceType<never>
    type T4 = InstanceType<string>  // Error: string 不滿足約束 abstract new (...args: any) => any
    type T5 = InstanceType<Function>  // Error: Function 不滿足約束 abstract new (...args: any) => any
    

ThisParameterType<Type>(函數參數this的類型)

  • 獲取函數參數的 this的類型晾咪,常用在call,apply贮配,bind中谍倦。如果函數內部的第一個參數命名不是this,則會返回unknown

    function getThis(this: string, a: number) {
      console.log(a, this);
    }
    type getThisType = ThisParameterType<typeof getThis>;   // string
    
    /****2******/
    function getThis(a: number) { console.log(a); }
    type getThisType = ThisParameterType<typeof getThis>;   // unknown
    
    /****3******/
    function fncTest(this: Number) {
      return this.toString(16);
    }
    function fncTestThis(n: ThisParameterType<typeof fncTest>) {   // (parameter) n: Number
      return fncTest.apply(n);
    }
    
  • 實現(xiàn):type ThisParameterType<T> = T extends (this: infer U, ...args: never) => any ? U : unknown;

OmitThisParameter<Type>

  • 獲取不帶this參數的新函數類型泪勒。同ThisParameterType<Type>昼蛀,this必須是函數的第一個參數。

    function getThis(this: string, a: number) {
      console.log(a, this);
    }
    type getThisType = OmitThisParameter<typeof getThis>;   // type getThisType = (a: number) => void
    
  • 實現(xiàn):type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T;

ThisType<Type>(上下文this類型的標記)

此實用程序不返回轉換后的類型圆存。相反,它充當上下文類型的標記叼旋。(Tips:必須啟用noImplicitThis標志才能使用此實用程序)

  • 官網示例與解釋:

    // Compile with --noImplicitThis
    type ObjectDescriptor<D, M> = {
      data?: D;
      methods?: M & ThisType<D & M>; // 方法中的“this”類型是 D & M
    };
    
    function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
      let data: object = desc.data || {};
      let methods: object = desc.methods || {};
      return { ...data, ...methods } as D & M;
    }
    
    let obj = makeObject({
      data: { x: 0, y: 0 },
      methods: {
        moveBy(dx: number, dy: number) {
          this.x += dx; // 強類型this
          this.y += dy; // 強類型this
        },
      },
    });
    
    obj.x = 10;
    obj.y = 20;
    obj.moveBy(5, 5);
    
    • 上示例中,makeObject參數中的對象屬性methods有一個上下文類型:ThisType<D & M>沦辙,因此對象中methods屬性下的方法中的this類型是{x: number, y: number} & {moveBy(dx: number, dy: number): void}夫植。

    • ThisType<T> 的接口,在 lib.d.ts 只是被聲明為空的接口油讯,除了可以在對象字面量上下文中可以被識別以外详民,該接口的作用等同于任意空接口延欠。

  • (簡單理解)

    • ThisType主要用于顯式指定對象方法中this的類型,在JSthis引用的是當前方法所在的對象沈跨,但在TS中衫冻,有時需要更準確的指定this的類型;例如

      • 當對象有多個方法時谒出,需要在每個方法中指定this的類型隅俘,以確保這些方法在使用this時不會發(fā)生類型錯誤;

      • 當對象方法作為參數傳遞給其它函數時笤喳,需要在類型中指定this類型为居,以確保在使用函數時this引用正確;

      interface Person {
        name: string;
        age: number;
        sayHi(): string;
      }
      
      // 使用 ThisType 來擴展 Person 接口并顯式指定 this 的類型
      type PersonWithGreeting = Person & ThisType<Person>;
      
      // greet中 使用 this: PersonWithGreeting 參數來指定 this 的類型
      function greet(this: PersonWithGreeting, greeting: string) {
        return `${greeting}, ${this.name}!`;
      }
      
      const person: PersonWithGreeting = {
        name: "Tom",
        age: 20,
        sayHi() {
          return `Hi, ${this.name}!`;
        },
      };
      
      // 最后杀狡,通過 call() 方法調用 greet 方法蒙畴,并指定 person 作為 this 對象,
      // 這樣呜象,在 greet 方法中膳凝,this 就表示 person 對象
      const greeting = greet.call(person, "Hello");
      
      console.log(greeting);        // Hello, Tom!
      console.log(person.sayHi());  // Hi, Tom!
      

內部字符串操作類型

  • Uppercase<StringType>StringType轉為大寫

  • Lowercase<StringType>StringType轉為小寫

  • Capitalize<StringType>StringType首字母大寫

  • Uncapitalize<StringType>StringType首字母小寫

type Eg1 = Uppercase<'abcd'>;     // type Eg1 = "ABCD"

type Eg2 = Lowercase<'ABCD'>;     // type Eg2 = "abcd"

type Eg3 = Capitalize<'Abcd'>;    // type Eg3 = "Abcd"

type Eg4 = Uncapitalize<'aBCD'>;  // type Eg4 = "aBCD"

擴展:自定義工具類型

1.獲取不同時存在于 TU 內的類型

實現(xiàn):type Exclusive<T, U> = Exclude<T | U, T & U>;

// 實現(xiàn)
type Exclusive<T, U> = Exclude<T | U, T & U>;

// 示例
type Eg = Exclusive<"a" | "b" | "c", "Er" | "a" | "b">   // type Eg = "c" | "Er"
  • 主要是利用Exclude獲取存在與第一個參數但不存在與第二個參數的類型
  • 參數二T & U 獲取的是所有類型的交叉類型;
  • 參數一T | U 這是利用在聯(lián)合類型在extends中的分發(fā)特性,可以理解為Exclude<T, T & U> | Exclude<U, T & U>;

2.獲取T中所有類型為函數的key組成的聯(lián)合類型

type FunctionKeys<T> = {
  [K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];

interface Test {
  foo(): void;
  bar: string;
  baz: () => number;
}

type TestFunction = FunctionKeys<Test>; // "foo" | "baz"
  • 實現(xiàn):

    • K in keyof T: 先使用 keyof T 獲取 T 中所有 key 組成的聯(lián)合類型, 然后使用 映射類型 將這個聯(lián)合類型轉換為一個新的對象

    • T[] 是索引訪問操作恭陡,可以取到值的類型蹬音;

    • T[K]為有效類型,則判斷是否為Function類型休玩,是的話返回K著淆,否則never

    • 最后執(zhí)行{...}[keyof T]索引訪問獲取最終結果

      • Tips1:T[keyof T] 則是獲取T所有值的類型拴疤;

      • Tips2:never 和其他類型進行聯(lián)合時永部,never 是不存在的,例如:never | number | string 等同于 number | string

3.獲取對象中指定類型的字段(由2改)

根據指定類型U呐矾,在對象T中挑選出所有類型一致的字段并組成一個新的類型苔埋。

type TypeKeys<T, U> = {
  [K in keyof T]: T[K] extends U ? K : never;
}[keyof T];

type PickByType<T, U> = Pick<T, TypeKeys<T, U>>;

interface Example {
  foo: string;
  bar: number;
  baz: string;
}

type StringFieldsOfExample = PickByType<Example, string>; // { foo: string, baz: string }
type NumberFieldsOfExample = PickByType<Example, number>; // { bar: number; }

4.從數組中提取指定屬性的值(由2/3改)

type PickKeys<T, K extends keyof T> = {
  [P in K]: T[P]
}[K];

function pluck<T, K extends keyof T>(arr: T[], key: K): PickKeys<T, K>[] {
  return arr.map((item) => item[key]);
}

interface Example3 {
  foo: string;
  bar: number;
}
const exampleList: Example3[] = [
  { foo: 'hello', bar: 1 },
  { foo: 'world', bar: 2 },
];

const fooList = pluck(exampleList, 'foo'); 
console.log('fooList', fooList);    // ["hello", "world"]

通過PickKeys從對象中提取指定屬性的值。定義pluck函數蜒犯,用于從數組中提取指定屬性的值组橄,并返回一個新的數組

5.查找T所有非只讀類型的key組成的聯(lián)合類型

/**
 * 核心實現(xiàn)
 */
type MutableKeys<T extends object> = {
  [P in keyof T]-?: IfEquals<
    { [Q in P]: T[P] },
    { -readonly [Q in P]: T[P] },
    P
  >;
}[keyof T];

/**
 * @desc 一個輔助類型,判斷X和Y是否類型相同愧薛,
 * @returns 是則返回A晨炕,否則返回B
 */
type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2)
  ? A
  : B;

示例:MutableKeys只適用一維對象,無法對嵌套對象生效

/**
 * 示例
 */
interface Person {
  readonly name: string;
  age: number;
  address: {
    readonly street: string;
    city: string;
  };
}


type T0 = MutableKeys<Person>   // type T0 = "age" | "address"

// 測試
type Test = Pick<Person, MutableKeys<Person>>
/**
 * =>
 * type Test = {
 *  age: number;    
 *  address: {       
 *    readonly street: string;
 *    city: string;
 *  };
 * }
 */
const person: Person = {
  name: "Alice",
  age: 30,
  address: {
    street: "123 Main St",
    city: "Springfield",
  },
};
const P2: Test = {
  age: 32,
  address: {
    street: "123 Main St",
    city: "Springfield",
  },
};

6.獲取T中所有的可選項或key

type OptionalKeys<T> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];

使用映射遍歷所有Key毫炉,通過Pick<T, K>提取當前Key和類型瓮栗;

利用{} extends {當前key: 類型}判斷是否為可選類型。

// Eg2 = false
type Eg2 = {} extends {key1: string} ? true : false;
// Eg3 = true
type Eg3 = {} extends {key1?: string} ? true : false;

利用的就是{}和只包含可選參數類型{key?: string}是兼容的這一特性。把extends前面的{}替換成object也是可以的费奸。

/**
 * 測試例
 */
interface E1 {
  a: string;
  b?: number;
  c?: boolean;
  c2: string;
}
/**
 * T 中所有可選項的key的聯(lián)合類型
 * type K1 = "b" | "c"
 */
type K1 = OptionalKeys<E1>;

/**
 * T 中所有可選項
 * type T1 = {
 *    b?: number | undefined;
 *    c?: boolean | undefined;
 * }
 */
type T1 = Pick<E1, K1>

7.Pick擴展:獲取T中指定類型的所有項

/**
 * 輔助函數
 * 用于獲取 T 中類型不為 never 的類型組成的聯(lián)合類型
 */
type TypeKeys<T> = T[keyof T];

/**
 * 核心實現(xiàn)
 * undefined 的存在 可以獲取可選項的值
 */
type PickByValue<T, V> = Pick<T,
  TypeKeys<{ [P in keyof T]: T[P] extends V | undefined ? P : never }>
>;


// 示例
interface E1 {
  a: string;
  a2?: string;
  b: number;
  b1: number | string;
  b2?: number;
}

type T1 = PickByValue<E1, string>
/**
 * type T1 = {
 *   a: string;
 *   a2?: string | undefined;
 * }
 * ----------------------------------------
 * 注意: 如果將 T[P] extends V | undefined 中的 undefined 去掉弥激,則拿到的值為
 *  type T1 = {
 *    a: string;
 *  }
 */

如果 T 中屬性的值可能是多個類型并且這些類型之間存在兼容性關系(比如 number | string),需要使用 Extract 類型來進行類型匹配愿阐。

type TypeKeys<T> = T[keyof T];

type PickByValueExact<T, V> = Pick<T,
  TypeKeys<{[P in keyof T]: Extract<T[P], V> extends never ? never : P}>
>;


interface E1 {
  a: string;
  a1: string | boolean;
  b: number;
  b1: number | string;
  c?: boolean;
}
type T1 = PickByValueExact<E1, boolean>
/**
 *  type T1 = {
 *    a1: string | boolean;
 *    c?: boolean | undefined;
 *  }
 */

8.刪除T中指定類型的所有項(由7改)

type TypeKeys<T> = T[keyof T];

type OmitByValueExact<T, V> = Omit<T,
  TypeKeys<{[P in keyof T]: Extract<T[P], V> extends never ? never : P}>
>;
/**
 * 等同于(Pick寫法) =>
 * type OmitByValueExact<T, V> = Pick<T,
 *  TypeKeys<{ [P in keyof T]: Extract<T[P], V> extends never ? P : never }>
 * >;
 */

// 示例
interface E1 {
  a: string;
  a1: string | boolean;
  b: number;
  b1: number | string;
  c?: boolean;
}
type T1 = OmitByValueExact<E1, boolean>
/**
 *  type T1 = {
 *    a: string;
 *    b: number;
 *    b1: number | string;
 *  }
 */

9.Pick擴展:獲取 TU共同存在key和對應類型

// T extends object, U extends object
type Intersection<T, U> = Pick<T, Extract<keyof U, keyof T> & Extract<keyof T, keyof U>>

// 使用
interface E1 {
  a: string;
  a1: string | boolean;
  b: number;
  b2?: number;
}

interface E2 {
  a: string;
  a1: boolean;
  b2?: number;
}
type T1 = Intersection<E1, E2>
// type T1 = {
//   a: string;
//   a1: string | boolean;
//   b2?: number | undefined;
// }

2次Extract的原因是為了避免類型的兼容推導問題微服。

10.去除T中存在于Ukey和對應類型(由9改)

type Diff<T, U> = Pick<T, Exclude<keyof T, keyof U>>

// 使用
interface E1 {
  a: string;
  a1: string | boolean;
  b: number;
  b2?: number;
}

interface E2 {
  a: string;
  a1: boolean;
  b2?: number;
  c: string
}

type T1 = Diff<E1, E2>
// type T1 = {
//   b: number;
// }

11.OverwriteAssign(9/10改)

  • Assign
// 合并,相同key和對應類型由后者覆蓋前者
type Assign<T, U, I = Diff<T, U> & U> = Pick<I, keyof I>;

// 示例
interface E1 {
  a: string;
  b: string | boolean;
}

interface E2 {
  a: string;
  c: string
}

type Eg2 = Assign<E1, E2>
// type Eg2 = {
//   a: string;
//   b: string | boolean;
//   c: string;
// }
  • Overwrite
// 獲取前者獨有的key和類型缨历,再取兩者共有的key和該key在后者中的類型以蕴,最后合并。
// T extends object, U extends object
type Overwrite<T, U, I = Diff<T, U> & Intersection<U, T>> = Pick<I, keyof I>

interface E1 {
  a: string;
  b3: number;
}

interface E2 {
  a: string;
  a1: boolean;
  b2: number;
  c: string
}
type Eg1 = Overwrite<E1, E2>
// type Eg1 = {
//   a: string;
//   b3: number;
// }

12.將聯(lián)合類型轉變成交叉類型

type UnionToIntersection<T> = (
  T extends any ? (arg: T) => void : never
) extends (arg: infer U) => void ? U : never;


interface A {
  a: string;
}

interface B {
  b: number;
}

type C = UnionToIntersection<A | B>; 
// { a: string } & { b: number }
  • T extends any ? (arg: T) => void : never: 如果類型 T 可以被轉化為任意類型辛孵,則返回一個接受參數為 T 類型的函數丛肮;否則返回 never 類型。

  • (arg: infer U) => void: infer關鍵字來推斷函數類型所接收的參數類型并定義一個新的類型U

  • 利用第二個extends配合infer推導得到U的類型魄缚,利用infer對協(xié)變類型的特性得到交叉類型宝与。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市冶匹,隨后出現(xiàn)的幾起案子习劫,更是在濱河造成了極大的恐慌,老刑警劉巖嚼隘,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诽里,死亡現(xiàn)場離奇詭異,居然都是意外死亡嗓蘑,警方通過查閱死者的電腦和手機须肆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門匿乃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桩皿,“玉大人,你說我怎么就攤上這事幢炸⌒垢簦” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵宛徊,是天一觀的道長佛嬉。 經常有香客問我,道長闸天,這世上最難降的妖魔是什么暖呕? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮苞氮,結果婚禮上湾揽,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好库物,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布霸旗。 她就那樣靜靜地躺著,像睡著了一般戚揭。 火紅的嫁衣襯著肌膚如雪诱告。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天民晒,我揣著相機與錄音精居,去河邊找鬼。 笑死潜必,一個胖子當著我的面吹牛箱蟆,可吹牛的內容都是我干的。 我是一名探鬼主播刮便,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼空猜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了恨旱?” 一聲冷哼從身側響起辈毯,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搜贤,沒想到半個月后谆沃,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡仪芒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年唁影,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掂名。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡据沈,死狀恐怖,靈堂內的尸體忽然破棺而出饺蔑,到底是詐尸還是另有隱情锌介,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布猾警,位于F島的核電站孔祸,受9級特大地震影響,放射性物質發(fā)生泄漏发皿。R本人自食惡果不足惜崔慧,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望穴墅。 院中可真熱鬧惶室,春花似錦匣屡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鹅士,卻和暖如春聪铺,著一層夾襖步出監(jiān)牢的瞬間课兄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留晒屎,地道東北人淫半。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓逆济,卻偏偏與公主長得像榛斯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子永票,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容