// 直接使用联合类型判断,ts会将其视为整体进行判断,而不是一个一个进行判断
type Test = string | number | boolean extends string ? string : never; // never
type Test2 = string extends string | number | boolean ? "yes" : "no"; // 'yes'
type CondType<T> = T extends string ? T : never;
// 可以发现,使用泛型的类型是一个一个进行比较的,只要有一个满足条件,就会返回这个类型
type Test3 = CondType<string | number | boolean>; // string
// 使用条件类型封装通用工具
type CommonType<T> = T extends object ? T : never;
// 所有需要约束的类型都可以使用这个工具进行约束,以后需要修改的话,只需要修改这个工具即可,不需要一个一个去修改
type PersonType = CommonType<Person>; // Person
type StringType = CommonType<string>; // never
type NumberType = CommonType<number>; // neverTypeScript 条件类型是一种强大的类型工具,允许你根据类型的特性来进行类型推断和转换。条件类型的语法类似于 JavaScript 中的三元运算符 condition ? trueType : falseType。以下是对你提供的代码的详细解释:
代码解释
// 直接使用联合类型判断,ts会将其视为整体进行判断,而不是一个一个进行判断
type Test = string | number | boolean extends string ? string : never; // never
type Test2 = string extends string | number | boolean ? "yes" : "no"; // 'yes'联合类型整体判断:
type Test = string | number | boolean extends string ? string : never; // never这里
string | number | boolean extends string被视为整体进行判断。因为联合类型string | number | boolean并不完全是string类型,所以结果是never。单个类型判断:
type Test2 = string extends string | number | boolean ? "yes" : "no"; // 'yes'这里
string extends string | number | boolean是单个类型判断。因为string是string | number | boolean的子类型,所以结果是"yes"。
type CondType<T> = T extends string ? T : never;
// 可以发现,使用泛型的类型是一个一个进行比较的,只要有一个满足条件,就会返回这个类型
type Test3 = CondType<string | number | boolean>; // string泛型条件类型:
type CondType<T> = T extends string ? T : never;这里
CondType是一个泛型条件类型。对于联合类型string | number | boolean,TypeScript 会逐个进行判断:string extends string ? string : never->stringnumber extends string ? number : never->neverboolean extends string ? boolean : never->never
所以
CondType<string | number | boolean>的结果是string。
// 使用条件类型封装通用工具
type CommonType<T> = T extends object ? T : never;
// 所有需要约束的类型都可以使用这个工具进行约束,以后需要修改的话,只需要修改这个工具即可,不需要一个一个去修改
type PersonType = CommonType<Person>; // Person
type StringType = CommonType<string>; // never
type NumberType = CommonType<number>; // never封装通用工具:
type CommonType<T> = T extends object ? T : never;这里
CommonType是一个通用的条件类型工具,用于约束类型为对象类型。如果T是对象类型,则返回T,否则返回never。CommonType<Person>->PersonCommonType<string>->neverCommonType<number>->never
条件类型的应用
条件类型在 TypeScript 中非常有用,特别是在以下场景中:
类型推断:根据条件推断出不同的类型。
类型转换:将一种类型转换为另一种类型。
类型保护:确保代码在运行时的类型安全。
类型映射:操作对象和数组类型。
示例
以下是一个简单的条件类型示例,用于提取函数的参数类型:
type ParametersType<T> = T extends (...args: infer P) => any ? P : never;
function exampleFunction(a: string, b: number): void {}
type ExampleParameters = ParametersType<typeof exampleFunction>; // [string, number]通过理解和掌握条件类型,可以编写更强大和灵活的 TypeScript 代码,提高代码的类型安全性和可维护性。
条件类型的复杂应用
在一个接口上自由添加属性组成新的类型,会有这样的需求,我们直接在接口上添加属性,可能会影响其它地方使用,或者这个接口是第三方提供的,这个时候可以应用到条件类型
interface Person {
name: string;
age: number;
location: string;
}
// 添加属性到接口上的工具
type AddProp<T, K extends string, V> = T & { [P in K]: V };
// 添加属性到Person接口上,这样得的是交叉类型,还不符合我们的要求
type PersonWithGender = AddProp<Person, "a", number>;
type AddPropToObj<T, K extends string, V> = {
[P in keyof T | K]: P extends keyof T ? T[P] : V;
};
// 可以发现属性a被添加到了Person接口上
type PersonWithGender2 = AddPropToObj<Person, "a", number>; // { name: string; age: number; location: string; a: number; }in keyof 与 keyof 综合应用
模板字符类型
// 模板字符串类型只可以使用 string | number | bigint | boolean | null | undefined
// 变通的方法是将泛型 T 与字符串组成交叉类型,这样就可以使用模板字符串类型
type StrTemplate<T> = `${T & string} is a string`;
type TestStr = StrTemplate<"aaa">; // 'aaa is a string'综合应用
type MethodsType = {
menu: {
title: string;
icon: string;
};
tabs: {
key: string;
content: string;
};
};
type Template<T, U> = `${T & string}/${U & string}`;
type TestTemplate = Template<"menu", "title" | "icon">;
type SpliceKeys<T extends object> = {
[K in keyof T]: Template<K, keyof T[K]>;
}[keyof T]; // 最后面的[keyof T]可以让[K in keyof T]舍去,只保留 Template<K, keyof T[K]> 的结果
type A = SpliceKeys<MethodsType>; // 'menu/title' | 'menu/icon' | 'tabs/key' | 'tabs/content'这里定义了一些类型来操作和组合字符串类型。
首先,Template<T, U> 是一个类型模板,它接受两个泛型参数 T 和 U,并将它们组合成一个字符串类型,格式为 ${T & string}/${U & string}。这里的 T & string 和 U & string 确保 T 和 U 都是字符串类型。这种类型模板在处理字符串拼接时非常有用。
接下来,TestTemplate 是 Template 类型的一个具体实例,它将 "menu" 和 "title" | "icon" 作为参数。结果是一个联合类型 "menu/title" | "menu/icon",表示可能的字符串组合。
当 U 是一个联合类型("title" | "icon")时,TypeScript 会将模板字面量类型应用到联合类型的每一个成员上。这意味着 Template<"menu", "title" | "icon"> 会被展开为:
${"menu" & string}/${"title" & string} | ${"menu" & string}/${"icon" & string}
这实际上等价于:
"menu/title" | "menu/icon"
然后,我们定义了 SpliceKeys<T extends object> 类型,它接受一个对象类型 T 作为参数。这个类型使用了映射类型 [K in keyof T] 来遍历 T 的所有键 K,并将每个键 K 与其对应值的键 keyof T[K] 组合成 Template<K, keyof T[K]> 类型。最后,通过 [keyof T],将所有这些组合的结果联合起来,形成一个新的联合类型。
最后,A 是 SpliceKeys<MethodsType> 的具体实例。MethodsType 是一个对象类型,包含两个键 menu 和 tabs,每个键对应一个对象。通过 SpliceKeys 类型,我们将 MethodsType 的键和值的键组合起来,得到 'menu/title' | 'menu/icon' | 'tabs/key' | 'tabs/content' 这样的联合类型。
这段代码展示了如何使用 TypeScript 的高级类型特性来动态生成字符串类型,极大地增强了类型系统的表达能力。