類型挑戰(zhàn)【轉(zhuǎn)載】:https://wangtunan.github.io/blog/typescript/challenge.html#%E4%BB%8B%E7%BB%8D
工具類型
Partial<Type>(可選)
-
解釋:用來構(gòu)造 (創(chuàng)建) 一個類型, 將
T的所有屬性設(shè)置為可選的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上的所有屬性 -
?:語法稱為映射修飾符,用于影響可選性。
-
-
常用場景一:構(gòu)造一個新類型
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的所有屬性都設(shè)置為readonly(只讀 ), 構(gòu)造出來的結(jié)構(gòu)完全相同,但所有屬性為只讀interface User { des: string; } // type ReadonlyUser = Readonly<User> const todo: Readonly<User> = { des: "A student", }; todo.des = "Hello"; // Error 類型值不可再賦值改變 -
內(nèi)部實現(xiàn):
type Readonly<T> = { readonly [P in keyof T]: T[P]; }- 主要實現(xiàn)是通過映射遍歷所有
key,然后給每個key增加一個readonly修飾符
- 主要實現(xiàn)是通過映射遍歷所有
Record<Keys, Type>
-
構(gòu)造一個對象類型,其屬性鍵為
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,將值設(shè)置為T;注意的是
keyof any得到的是string | number | symbol,原因在于類型key的類型只能為string | number | symbol
Pick<Type, Keys>(挑選)
-
從
Type中選取一組屬性Keys(字符串字面值或字符串字面值的并集)來構(gòu)造一個新類型。- 即:獲取一個類型中的某些
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(字符串字面值或字符串字面值的并集)來構(gòu)造類型。- 即:移除一個類型的某些
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后,剩下的屬性構(gòu)成一個新的類型,即‘我們需要的屬性聯(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]設(shè)置類型為原來的類型
- 類似
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中排除null和undefined來構(gòu)造一個類型。-
實現(xiàn):
type NonNullable<T> = T & {};type T0 = NonNullable<string | number | undefined | null>; // => type T0 = string | number
Parameters<Type>(參數(shù)的類型)
返回由函數(shù)參數(shù)類型組成的元組類型
-
實現(xiàn):
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;首先約束參數(shù)
T必須是一個函數(shù)類型,所以(...args: any) => any替換成Function也是可以的;具體實現(xiàn):判斷
T是否是個函數(shù)類型,如果是則使用infer P讓TS自己推導(dǎo)出函數(shù)的參數(shù)類型,并將推導(dǎo)結(jié)果存到類型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
關(guān)鍵詞
infer的作用是讓TS自己推導(dǎo)類型,并將推導(dǎo)結(jié)果存儲在其參數(shù)綁定的類型上。infer只能在extends條件類型上使用,不能在其它地方使用。-
擴展:
infer實現(xiàn)一個推導(dǎo)數(shù)組所有元素的類型:/** * 約束參數(shù)T為數(shù)組類型, * 判斷T是否為數(shù)組,如果是數(shù)組類型則推導(dǎo)數(shù)組元素的類型 */ 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>(返回值類型)
獲取函數(shù)的返回值類型
-
實現(xiàn):
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any- 和
Parameters<Type>相比,ReturnType<Type>只是將infer R從參數(shù)位置移到返回值位置,此時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>(獲取構(gòu)造函數(shù)參數(shù)類型)
可以獲取類的構(gòu)造函數(shù)的參數(shù)類型,存在一個元組中。(如果
type不是函數(shù),則為never類型)。-
實現(xiàn):
type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never核心還是用
infer推導(dǎo)構(gòu)造函數(shù)的參數(shù)類型首先約束參數(shù)
T是擁有構(gòu)造函數(shù)的類,判斷T是滿足約束的類時,利用infer P自動推導(dǎo)構(gòu)造函數(shù)的參數(shù)類型,并最終返回該類型。new (...args: any)是構(gòu)造簽名,new (...args: any) => any是構(gòu)造函數(shù)類型字面量
-
示例:
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: 無法將 抽象構(gòu)造函數(shù)類型 分配給 非抽象構(gòu)造函數(shù)類型。
const TAbs1: typeof TestAbst = Test // 可以賦值
const TAbs2: typeof TestAbst = TestAbst // 可以賦值
- 可以將抽象類(抽象構(gòu)造函數(shù))賦值給抽象類或者普通類,反之不行。
擴展2:關(guān)于類類型的表示
如上const T1: typeof Test = Test,雖然用typeof表示類的類型非常方便(其實它不是用來干這個的, 至少不全是),但不能因每次要寫一個類類型時都先用class關(guān)鍵詞定義一個類。
替代方法:使用構(gòu)造函數(shù)表示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>(獲取構(gòu)造函數(shù)返回值的類型)
InstanceType用于獲取類的實例化類型,即 new 類 的產(chǎn)物,可以說是執(zhí)行構(gòu)造函數(shù)的返回值,與上面的ConstructorParameters類型相對應(yīng)
-
實現(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>(函數(shù)參數(shù)this的類型)
-
獲取函數(shù)參數(shù)的
this的類型,常用在call,apply,bind中。如果函數(shù)內(nèi)部的第一個參數(shù)命名不是this,則會返回unknownfunction 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參數(shù)的新函數(shù)類型。同ThisParameterType<Type>,this必須是函數(shù)的第一個參數(shù)。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類型的標記)
此實用程序不返回轉(zhuǎn)換后的類型。相反,它充當上下文類型的標記。(Tips:必須啟用noImplicitThis標志才能使用此實用程序)
-
官網(wǎng)示例與解釋:
// 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參數(shù)中的對象屬性methods有一個上下文類型:ThisType<D & M>,因此對象中methods屬性下的方法中的this類型是{x: number, y: number} & {moveBy(dx: number, dy: number): void}。ThisType<T>的接口,在lib.d.ts只是被聲明為空的接口,除了可以在對象字面量上下文中可以被識別以外,該接口的作用等同于任意空接口。
-
(簡單理解)
-
ThisType主要用于顯式指定對象方法中this的類型,在JS中this引用的是當前方法所在的對象,但在TS中,有時需要更準確的指定this的類型;例如當對象有多個方法時,需要在每個方法中指定
this的類型,以確保這些方法在使用this時不會發(fā)生類型錯誤;當對象方法作為參數(shù)傳遞給其它函數(shù)時,需要在類型中指定
this類型,以確保在使用函數(shù)時this引用正確;
interface Person { name: string; age: number; sayHi(): string; } // 使用 ThisType 來擴展 Person 接口并顯式指定 this 的類型 type PersonWithGreeting = Person & ThisType<Person>; // greet中 使用 this: PersonWithGreeting 參數(shù)來指定 this 的類型 function greet(this: PersonWithGreeting, greeting: string) { return `${greeting}, ${this.name}!`; } const person: PersonWithGreeting = { name: "Tom", age: 20, sayHi() { return `Hi, ${this.name}!`; }, }; // 最后,通過 call() 方法調(diào)用 greet 方法,并指定 person 作為 this 對象, // 這樣,在 greet 方法中,this 就表示 person 對象 const greeting = greet.call(person, "Hello"); console.log(greeting); // Hello, Tom! console.log(person.sayHi()); // Hi, Tom!
-
內(nèi)部字符串操作類型
Uppercase<StringType>將StringType轉(zhuǎn)為大寫Lowercase<StringType>將StringType轉(zhuǎn)為小寫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.獲取不同時存在于 T 和 U 內(nèi)的類型
實現(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獲取存在與第一個參數(shù)但不存在與第二個參數(shù)的類型 - 參數(shù)二
T & U獲取的是所有類型的交叉類型; - 參數(shù)一
T | U這是利用在聯(lián)合類型在extends中的分發(fā)特性,可以理解為Exclude<T, T & U> | Exclude<U, T & U>;
2.獲取T中所有類型為函數(shù)的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)合類型轉(zhuǎn)換為一個新的對象T[]是索引訪問操作,可以取到值的類型;若
T[K]為有效類型,則判斷是否為Function類型,是的話返回K,否則never;-
最后執(zhí)行
{...}[keyof T]索引訪問獲取最終結(jié)果Tips1:
T[keyof T]則是獲取T所有值的類型;Tips2:
never和其他類型進行聯(lián)合時,never是不存在的,例如:never | number | string等同于number | string
3.獲取對象中指定類型的字段(由2改)
根據(jù)指定類型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.從數(shù)組中提取指定屬性的值(由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函數(shù),用于從數(shù)組中提取指定屬性的值,并返回一個新的數(shù)組
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;
利用的就是{}和只包含可選參數(shù)類型{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中指定類型的所有項
/**
* 輔助函數(shù)
* 用于獲取 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 中屬性的值可能是多個類型并且這些類型之間存在兼容性關(guān)系(比如 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擴展:獲取 T和U共同存在key和對應(yīng)類型
// 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的原因是為了避免類型的兼容推導(dǎo)問題。
10.去除T中存在于U的key和對應(yīng)類型(由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.Overwrite 和 Assign(9/10改)
Assign
// 合并,相同key和對應(yīng)類型由后者覆蓋前者
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)合類型轉(zhuǎ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可以被轉(zhuǎn)化為任意類型,則返回一個接受參數(shù)為T類型的函數(shù);否則返回never類型。(arg: infer U) => void:infer關(guān)鍵字來推斷函數(shù)類型所接收的參數(shù)類型并定義一個新的類型U利用第二個
extends配合infer推導(dǎo)得到U的類型,利用infer對協(xié)變類型的特性得到交叉類型。