[TS] Type Manipulation
keywords: generics, conditional type, Mapped Types, keyof, typeof, infer
Conditional Type
Conditional Types @ TypeScript > Handbook > Type Manipulation
// 如果 SomeType 能夠滿足 OtherType 則 SomeType 為 TrueType;否則為 FalseType
NewType = SomeType extends OtherType ? TrueType : FalseType;
要看是否符合某個條件,可以用:
// type T = [想檢驗的條件] ? true : false
type T = 30 extends number ? true : false;
基本語法
// https://www.typescriptlang.org/docs/handbook/2/conditional-types.html
interface IdLabel {
id: number /* some fields */;
}
interface NameLabel {
name: string /* other fields */;
}
type NameOrId<T extends number | string> = T extends number ? IdLabel : NameLabel;
function createLabel<T extends number | string>(idOrName: T): NameOrId<T> {
throw 'unimplemented';
}
let a = createLabel('typescript'); // a is NameLabel
let b = createLabel(2.8); // b is IdLabel
let c = createLabel(Math.random() ? 'Hello' : 42); // c is NameLabel | IdLabel
let d = createLabel(['foo']); // Type 'string[]' is not assignable to type 'string'
提示
T extends K 可以想成是 T 能夠滿足 K,也就是 T 是 K 的子集合(subset),K 的條件會比 T 來的寬鬆。
Conditional Type Constraints
Example: MessageOf
如果想要建立一個名為 MessageOf 的 Type,且其 Type 會是 T 中 message property 的型別:
// 因為 TypeScript 無法確定 T 一定有 message 這個 property 所以會噴錯
type MessageOfWithError<T> = T['message'];
// 透過 extends 確保 T 一定會有 message 這個 property
// 如此 MessageOf 這個 type 的型別就是會 T 當中 message property 的型別
type MessageOf<T extends { message: unknown }> = T['message'];
根據定義好的 MessageOf 這個 utility type,可以產生新的 Type:
// 因為 TypeScript 無法確定 T 一定有 message 這個 property 所以會噴錯
type MessageOfWithError<T> = T['message'];
// 透過 extends 確保 T 一定會有 message 這個 property
// 如此 MessageOf 這個 type 的型別就是會 T 當中 message property 的型別
type MessageOf<T extends { message: unknown }> = T['message'];
interface Email {
message: string;
}
interface Dog {
bark(): void;
}
// EmailMessageContents 會取得 Email 這個 Type 中 message property 的型別
type EmailMessageContents = MessageOf<Email>; // string
Example: Flatten
建立一個 utility type 可以取得另一個陣列型別內元素的型別:
// 如果 T 是陣列,則使用陣列元素的型別,否則使用 T
// 這裡 T[number] 使用的是 indexed 的方式來存取某陣列型別內元素的型別
type Flatten<T> = T extends any[] ? T[number] : T;
type Str = Flatten<string[]>; // Str 會是 string type;
type Num = Flatten<number>; // Num 會是 number type;
Example: Wrap
type Wrap<T> = T extends { length: number } ? [T] : T;
type stringArr = Wrap<string>;