TypeScript,这门优雅的编程语言,以其静态类型系统为 JavaScript 世界带来了秩序与和谐。在 TypeScript 的武器库中,infer 和 extends 是两把锋利的剑,它们在类型的世界里舞动,为开发者提供了强大的类型推断和限制能力。
infer:类型推断的魔术师
infer 是 TypeScript 3.7 版本中引入的关键字,它在条件类型中扮演着魔术师的角色,能够巧妙地从泛型类型参数中变出一个更具体的类型。
使用场景:
- 设计泛型函数时,希望它能够智能地根据传入的参数类型来返回一个相关的类型。
- 在类型别名或接口中,基于已有的类型参数推导出新的类型。
如何使用:
type ExtractReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function myFunction(): number {
return 1;
}
type MyFunctionReturnType = ExtractReturnType<typeof myFunction>; // MyFunctionReturnType 被推断为 number
在这个例子中,ExtractReturnType 利用 infer 巧妙地推断出函数 myFunction 的返回类型。
extends:类型限制的守护者
extends 在 TypeScript 中扮演着守护者的角色,它确保类型参数符合我们的期望,不会偏离我们设定的轨迹。
使用场景:
- 定义泛型函数或类时,希望限制传入的类型参数必须是某个类型或其子类型。
- 确保一个类型是另一个类型的子集,维护类型的一致性和预期行为。
如何使用:
interface Person {
name: string;
age: number;
}
interface Employee extends Person {
department: string;
}
let employee: Employee = {
name: "John Doe",
age: 30,
department: "Engineering"
};
在这个例子中,Employee 接口通过 extends 关键字继承了 Person 接口的属性,并添加了额外的属性。
何时使用 infer 和 extends
- 使用
infer的情况通常涉及到泛型和复杂的类型操作,当你需要基于已有的复杂类型结构推导出新的类型时,infer是一个非常有用的工具。 - 使用
extends的情况则是当你需要定义一个类型,这个类型是另一个类型的特定形式或扩展时。它常用于确保类型的兼容性和约束。
常见案例
- 泛型约束:使用
extends来约束泛型参数,确保它们符合特定的结构或具有某些属性。
function log<T extends object>(item: T): void {
console.log(item);
}
- 高级类型守卫:使用
infer来创建更精确的类型守卫,这在处理复杂类型时非常有用。
type OptionalKeys<T> = {
[K in keyof T]-?: undefined extends T[K] ? K : never;
}[keyof T];
interface Data {
requiredField: string;
optionalField?: number;
}
type Result = OptionalKeys<Data>; // "Result" 被推断为 "optionalField"
在上述案例中,OptionalKeys 利用 infer 和条件类型来推断出 Data 接口中所有的可选属性键。
通过合理地使用 infer 和 extends,TypeScript 允许开发者编写出更加健壮和可维护的类型定义,从而提高代码的类型安全性。