Skip to content

类型提取 (Type Extraction)

类型提取是指从现有类型中提取出特定信息,如属性、方法或键等,以创建新的类型。TypeScript 提供了多种工具和操作符来帮助开发者进行类型提取,从而构建更灵活和强大的类型系统。以下是几种常见的类型提取方式及其使用场景。

1. keyof 操作符

keyof 是最常用的类型提取操作符之一,它可以从一个类型中提取出所有键的联合类型。

typescript
interface Person {
  name: string;
  age: number;
}

type Keys = keyof Person; // "name" | "age"

2. 索引访问类型

索引访问类型允许你根据键名从类型中提取出相应的属性类型。

typescript
type NameType = Person["name"]; // string
type AgeType = Person["age"]; // number

你可以结合 keyof 和索引访问类型来动态地提取属性类型:

typescript
type GetPropType<T, K extends keyof T> = T[K];

type PersonName = GetPropType<Person, "name">; // string
type PersonAge = GetPropType<Person, "age">; // number

3. 映射类型

映射类型允许你基于现有类型的键创建新的类型,并可以对每个键应用转换规则。

typescript
type PartialPerson = {
  [P in keyof Person]?: Person[P];
};

const partialPerson: PartialPerson = {
  name: "Alice",
  // age and address are optional here
};

映射类型还可以用于修改属性的可选性、只读性等特性:

  • Partial<T>:将所有属性变为可选。
  • Readonly<T>:将所有属性变为只读。
  • Record<K, T>:创建一个新类型,其中键为 K 的联合类型,值为 T
typescript
type ReadonlyPerson = { readonly [P in keyof Person]: Person[P] };

const readonlyPerson: ReadonlyPerson = {
  name: "Bob",
  age: 30,
};
// readonlyPerson.name = "Charlie"; // Error: Cannot assign to 'name' because it is a read-only property.

4. 条件类型

条件类型可以根据某些条件来决定返回哪种类型。这在需要根据类型特征进行分支选择时非常有用。

typescript
type IsString<T> = T extends string ? true : false;

type StringCheck = IsString<string>; // true
type NumberCheck = IsString<number>; // false

条件类型也可以用来提取满足特定条件的属性:

typescript
type FunctionPropertyNames<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
}[keyof T];

type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;

interface Part {
  id: number;
  getName(): string;
}

type FuncProps = FunctionProperties<Part>; // { getName: () => string }

5. 模板字面量类型

模板字面量类型允许你通过字符串模板创建新的类型,这对于提取或组合字符串类型的键非常有用。

typescript
type EventConfig = {
  onMouseEnter: string;
  onMouseLeave: string;
};

type ExtractEventPrefix<T> = T extends `on${infer U}` ? U : never;

type EventPrefixes = ExtractEventPrefix<keyof EventConfig>; // "MouseEnter" | "MouseLeave"

6. 工具类型

TypeScript 内置了一些工具类型,这些工具类型可以帮助你快速进行类型提取和转换:

  • Pick<T, K>:从 T 中选取指定的键 K
  • Omit<T, K>:从 T 中移除指定的键 K
  • Extract<T, U>:从 T 中提取出与 U 兼容的成员。
  • Exclude<T, U>:从 T 中移除与 U 兼容的成员。
typescript
type PickPerson = Pick<Person, "name">; // { name: string }

type OmitPerson = Omit<Person, "age">; // { name: string }

7. as const

as const 是一种特殊的类型断言,它允许你将一个值标记为常量,并允许 TypeScript 在编译时进行更严格的类型检查。

typescript
const keys = ["host", "port", "debug"] as const;
// 不加 as const,keys 的类型为 string[]
// 加了后,keys 的类型为 ["host", "port", "debug"]

type Keys = (typeof keys)[number];
//不加 as const,Keys 的类型为 string
//加了后,Keys 类型为 "host" | "port" | "debug"

实际应用案例

假设我们有一个配置对象,希望创建一个函数来安全地更新其属性:

typescript
interface Config {
  host: string;
  port: number;
  debug: boolean;
}

function updateConfig<K extends keyof Config>(
  config: Config,
  key: K,
  value: Config[K]
): void {
  config[key] = value;
}

const config: Config = {
  host: "localhost",
  port: 8080,
  debug: true,
};

updateConfig(config, "host", "127.0.0.1"); // OK
updateConfig(config, "port", 9000); // OK
updateConfig(config, "debug", false); // OK
// updateConfig(config, "unknown", "value"); // Error, "unknown" is not a valid key of Config

在这个例子中:

  • keyof Config 提取了 Config 接口的所有键。
  • K extends keyof Config 确保了 key 参数只能是 Config 接口中的有效键。
  • Config[K] 使用索引访问类型确保 value 参数的类型与 key 对应的属性类型匹配。

总结

类型提取是 TypeScript 中一种强大的功能,它允许你从现有类型中提取出特定信息,创建新的类型,或者对现有类型进行转换。通过合理使用 keyof、索引访问类型、映射类型、条件类型、模板字面量类型以及内置工具类型,你可以构建更加健壮和灵活的类型系统,提高代码的安全性和可维护性。

Released under the MIT License.