高阶类型是TypeScript中一种高级的类型操作,它允许你创建或修改现有类型的结构。这些类型通常作为其他类型的参数(即“泛型”)来定义新的类型。以下是一些常见的高阶类型示例:
Partial 类型会将 T 中的所有属性都变为可选: - interface Person {
- name: string;
- age: number;
- }
-
- type PartialPerson = Partial<Person>;
- // 等同于 { name?: string; age?: number; }
Pick 从类型 T 中选择指定键 K 的属性构成新类型。 - interface Person {
- name: string;
- age: number;
- address: string;
- }
-
- type NameAndAge = Pick<Person, 'name' | 'age'>;
- // 等同于 { name: string; age: number; }
Omit 则是从类型 T 中排除指定键 K 的属性。 - type WithoutAddress = Omit<Person, 'address'>;
- // 等同于 { name: string; age: number; }
K 类型表示,值都是由 V 类型表示。 - type UserMap = Record<string, Person>;
- // 等同于 { [key: string]: Person; }
type KeysOfPerson = keyof Person; // "name" | "age" | "address" infer 在条件类型中用于推断类型。例如,在声明泛型时可以使用 infer 来从匹配的类型中提取子类型。
- type Unpacked<T> = T extends (infer U)[] ? U : T;
- // 此类型用于从数组类型中提取元素类型
3.Conditional Types(条件类型): 允许根据某些条件表达式来确定类型
- type TypeName
= T extends string ? 'string' : - T extends number ? 'number' :
- T extends boolean ? 'boolean' :
- 'unknown';
通过这些高阶类型,开发者可以在TypeScript中实现更复杂、更灵活的类型定义和转换,进一步提升代码的类型安全性和可读性。
infer 是 TypeScript 中用于在条件类型(Conditional Types)中进行类型推断的关键字。它的主要作用是根据某种条件来提取或推断出未知类型的值。
下面是一个更具体的 infer 示例,解释如何从泛型参数中提取类型:
- type MyPair<T> = [T, T];
-
- // 使用 infer 关键字定义一个 ExtractType 类型,它能从 MyPair 类型中提取出元素类型
- type ExtractType<T> = T extends MyPair<infer U> ? U : never;
-
- // 测试用例
- let pair1: MyPair<number> = [1, 2];
- type NumberFromPair = ExtractType<typeof pair1>; // NumberFromPair 的类型被推断为 number
-
- let pair2: MyPair<string> = ['hello', 'world'];
- type StringFromPair = ExtractType<typeof pair2>; // StringFromPair 的类型被推断为 string
在这个例子中:
MyPair,表示一个包含两个相同类型元素的数组。ExtractType 这个条件类型,使用 infer 来推断 MyPair 中的元素类型 U。T 能够匹配 MyPair 形式时,ExtractType 的结果就是 U 类型;否则结果是 never 类型(表示不可能存在的类型)。通过这种方式,我们可以从已知类型中“提取”或“推断”出内部嵌套的未知类型,这对于处理复杂的类型关系非常有用。
这部分并不是固定的写法,但它是一种常见的 infer 关键字与条件类型结合的用法。
这里的结构遵循了 TypeScript 中条件类型的语法:
T extends U ? X : Y
其中:
T 是要检查的类型。U 是用于比较的基础类型或类型构造器(在这个例子中是 MyPair)。X 是当 T 扩展自(或者说匹配)U 时的结果类型。Y 是当 T 不扩展自(不匹配)U 时的结果类型。在 这个表达式中:
infer U 表示我们正在尝试从 T 中推断出一个未知类型 U。T 可以成功地被解释为 MyPair<某种类型> 的形式时,infer U 将能够推断出那个“某种类型”,并将其作为结果类型返回,即 U。T 不能被解释为此形式,则结果类型是 never,表示不存在这样的类型。所以这不是固定写法,但这种模式对于基于泛型或其他复杂类型进行类型推断的场景非常常见。根据具体需求,infer 和条件类型的具体使用可能会有所不同。
extends 在 TypeScript 中有多种含义和用法,主要体现在以下两个方面:
extends 关键字实现类之间的继承关系。子类会继承父类的属性和方法。 - class Animal {
- name: string;
- constructor(name: string) {
- this.name = name;
- }
- makeSound(): void {
- console.log('Making a sound');
- }
- }
-
- class Dog extends Animal {
- breed: string;
-
- constructor(name: string, breed: string) {
- super(name);
- this.breed = breed;
- }
-
- bark(): void {
- console.log('Woof woof!');
- }
- }
extends 用于判断一个类型是否兼容或扩展自另一个类型。这在泛型约束、接口继承以及条件类型中非常有用。
- function identity<T extends string>(arg: T): T {
- return arg;
- }
上述代码中的 `T extends string` 表示传入的泛型参数 `T` 必须是字符串类型或者其子类型。
type Unpacked<T> = T extends (infer U)[] ? U : T; 此处的 `extends` 用于在条件类型表达式中进行类型检查,如果 `T` 扩展自数组类型,则推断并返回数组元素类型 `U`,否则返回 `T` 类型本身。
总结来说,extends 是 TypeScript 中用来表示继承和类型兼容性的重要关键字,在面向对象编程和类型系统的复杂逻辑处理中起着关键作用。
Partial 是 TypeScript 中的一个内置工具类型,它的作用就是将类型 T 中的所有属性都变为可选(optional)。
例如,假设我们有一个接口:
- interface Person {
- name: string;
- age: number;
- address?: string; // 这个已经是可选属性了
- }
如果我们想创建一个新的类型,其中所有的属性都是可选的,可以使用 Partial 类型:
- type OptionalPerson = Partial<Person>;
-
- // 等同于
- // type OptionalPerson = {
- // name?: string;
- // age?: number;
- // address?: string;
- // }
这样一来,当你在代码中使用 OptionalPerson 类型时,就不需要为任何属性提供值。而如果某个变量声明为 Person 类型,则必须为其所有非可选属性赋值。
Partial 的核心功能就是将类型 T 中的所有属性(包括索引签名和方法)都变为可选。这意味着当你使用 Partial 时,不需要为这些属性提供值就可以创建该类型的对象实例。
虽然从功能上看,Partial 主要是为了实现属性转可选,但它的应用场景非常广泛,尤其是在处理可能不完整的数据结构、更新现有对象的部分属性或与外部接口交互(如API请求响应可能只返回部分数据)等方面,能有效提高代码的灵活性和健壮性。
keyof T 是 TypeScript 中用于获取类型 T 的所有公共属性键的类型操作符。它会返回一个联合类型,该联合类型包含 T 类型中所有可索引的公共属性名。
- interface Person {
- name: string;
- age: number;
- address?: string; // 可选属性
- }
-
- type PersonKeys = keyof Person; // 等同于 "name" | "age" | "address"
在这个例子中,PersonKeys 类型包含了 Person 接口的所有属性名称(包括可选属性)作为字符串字面量类型。
当你使用 keyof T 时,TypeScript 会生成一个表示对象所有可能键的类型,这对于编写更灵活且类型安全的代码非常有用,尤其是在处理映射、枚举对象属性或创建基于现有类型的接口和函数参数时。
当你在 TypeScript 中使用 keyof T 这个操作符时,TypeScript 编译器会分析类型 T 的结构,并生成一个新的类型,这个新类型包含了所有可以作为 T 类型对象的属性键(即键名)。
- interface Person {
- name: string;
- age: number;
- address?: string; // 可选属性
- }
-
- type KeysOfPerson = keyof Person;
-
- // 此时,KeysOfPerson 类型被推断为 "name" | "age" | "address"
在这个例子中,KeysOfPerson 类型代表了可以从 Person 接口中访问的所有合法属性名称。这意味着任何变量如果声明为 KeysOfPerson 类型,它可以是 "name"、"age" 或 "address"。
这种类型的用途很多,比如在编写映射函数、创建动态属性访问或约束函数参数必须是给定类型的有效属性名等场景。
keyof T 主要用来获取类型 T 的所有合法键名,并生成一个可以表示这些键名联合类型的类型。这个类型主要用于检查和限制变量必须是给定对象类型的所有可能键名之一,确保在代码中使用的属性名是有效的、与类型 T 匹配的。它有助于提高代码的健壮性和可维护性,尤其是在处理动态属性访问或映射操作等场景时。