泛型约束
泛型约束的基本概念
TypeScript中的泛型约束允许我们对泛型类型参数施加限制,确保它们满足特定条件。通过extends
关键字,我们可以指定泛型必须符合某种类型结构。这种机制在需要确保泛型参数具备某些属性或方法时特别有用。
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
使用类型参数约束
泛型约束不仅可以基于具体类型,还可以基于其他类型参数。这种技术常用于创建依赖关系的泛型函数,其中一个类型参数必须满足另一个类型参数的要求。
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3 };
getProperty(x, "a"); // 正确
getProperty(x, "m"); // 错误:'m'不在'a'|'b'|'c'中
多重约束的应用
TypeScript支持对泛型参数施加多重约束,通过交叉类型实现。这种方式要求类型参数必须同时满足多个接口或类型的条件。
interface Serializable {
serialize(): string;
}
interface Loggable {
log(): void;
}
function process<T extends Serializable & Loggable>(item: T) {
item.log();
const serialized = item.serialize();
// ...
}
构造函数约束
通过new
约束,我们可以限制泛型类型必须具有构造函数。这在需要创建类型实例的工厂函数中特别有用。
interface Constructor<T> {
new(...args: any[]): T;
}
function createInstance<T>(ctor: Constructor<T>, ...args: any[]): T {
return new ctor(...args);
}
class Person {
constructor(public name: string) {}
}
const person = createInstance(Person, "Alice");
约束与条件类型的结合
泛型约束与条件类型结合使用时,可以创建更灵活的类型操作。这种组合允许基于约束条件选择不同的类型分支。
type Flatten<T> = T extends Array<infer U> ? U : T;
function flatten<T>(array: T): Flatten<T> {
return Array.isArray(array) ? array[0] : array;
}
const str = flatten("hello"); // string
const num = flatten([42]); // number
高级约束模式
在复杂场景中,我们可以使用映射类型和keyof操作符创建高级约束模式。这些技术常用于构建类型安全的实用工具类型。
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
interface User {
id: number;
name: string;
age: number;
}
type PartialUser = PartialBy<User, 'id' | 'age'>;
// 等价于 { name: string; id?: number; age?: number }
约束在React组件中的应用
在React开发中,泛型约束常用于创建可复用组件,同时确保props类型的安全性。这种模式在高阶组件和渲染prop组件中尤为常见。
interface WithLoadingProps {
loading: boolean;
}
function withLoading<P extends object>(
Component: React.ComponentType<P>
): React.FC<P & WithLoadingProps> {
return ({ loading, ...props }: P & WithLoadingProps) =>
loading ? <div>Loading...</div> : <Component {...props as P} />;
}
类型谓词与约束
结合类型谓词,泛型约束可以用于创建用户定义的类型保护函数,这些函数能够在运行时检查类型并缩小类型范围。
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
function isBird<T extends Bird | Fish>(pet: T): pet is T & Bird {
return (pet as Bird).fly !== undefined;
}
function move<T extends Bird | Fish>(pet: T) {
if (isBird(pet)) {
pet.fly();
} else {
pet.swim();
}
}
约束与默认类型参数
泛型约束可以与默认类型参数结合使用,提供更灵活的API设计。当类型参数未显式指定时,将使用默认类型,但仍需满足约束条件。
interface PaginationOptions<T = any> {
pageSize: number;
currentPage: number;
data: T[];
}
function paginate<T extends { id: string } = { id: string }>(
options: PaginationOptions<T>
) {
// 实现分页逻辑
}
递归类型约束
TypeScript支持递归类型约束,允许类型参数约束引用自身。这种技术常用于定义树形结构或递归数据结构。
interface TreeNode<T extends TreeNode<T>> {
value: number;
children: T[];
}
class BinaryNode implements TreeNode<BinaryNode> {
constructor(
public value: number,
public children: [BinaryNode?, BinaryNode?] = []
) {}
}
约束与可变元组类型
在TypeScript 4.0引入的可变元组类型中,泛型约束可以用于限制元组元素的类型关系,创建更精确的元组操作函数。
type Shift<T extends any[]> = ((...args: T) => any) extends
((arg: any, ...rest: infer R) => any) ? R : never;
function tail<T extends any[]>(args: [...T]): Shift<T> {
const [, ...rest] = args;
return rest as Shift<T>;
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn