类型参数约束
类型参数约束的基本概念
TypeScript的类型参数约束允许我们限制泛型类型参数的范围。通过在泛型类型参数后面使用extends
关键字,可以指定该类型参数必须满足的条件。这种机制确保了类型安全,同时保持了泛型的灵活性。
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
单一约束与多重约束
类型参数可以受单一接口或类型的约束,也可以通过联合类型实现多重约束。当需要满足多个条件时,可以使用交叉类型。
// 单一约束
function process<T extends string>(value: T): void {
console.log(value.toUpperCase());
}
// 多重约束
interface Serializable {
serialize(): string;
}
interface Identifiable {
id: number;
}
function persist<T extends Serializable & Identifiable>(item: T): void {
const serialized = item.serialize();
localStorage.setItem(`item_${item.id}`, serialized);
}
约束与默认类型结合
类型参数约束可以与默认类型结合使用,这在创建灵活且类型安全的API时特别有用。
interface PaginationOptions<T = any> {
pageSize?: number;
currentPage?: number;
filter?: T extends object ? Partial<T> : never;
}
function paginate<T extends object = {}>(options: PaginationOptions<T> = {}) {
// 实现逻辑
}
使用keyof进行属性约束
keyof
操作符常与类型约束一起使用,确保只能访问对象上存在的属性。
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "Alice", age: 30 };
getProperty(person, "name"); // 正确
getProperty(person, "gender"); // 错误:gender不是person的属性
构造函数约束
通过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 alice = createInstance(Person, "Alice");
递归类型约束
类型约束可以用于定义递归类型,这在处理树形结构或嵌套数据时非常有用。
interface TreeNode<T> {
value: T;
children?: TreeNode<T>[];
}
function walkTree<T>(node: TreeNode<T>, callback: (value: T) => void) {
callback(node.value);
node.children?.forEach(child => walkTree(child, callback));
}
条件类型中的约束
在条件类型中使用约束可以创建更复杂的类型逻辑。
type NonNullablePropertyKeys<T> = {
[K in keyof T]: null extends T[K] ? never : K
}[keyof T];
interface User {
name: string;
age: number | null;
email: string | null;
}
type RequiredUserKeys = NonNullablePropertyKeys<User>; // "name"
约束与映射类型
结合映射类型,约束可以用于转换对象类型的属性。
type ReadonlyProps<T, K extends keyof T> = {
readonly [P in K]: T[P];
} & {
[P in Exclude<keyof T, K>]: T[P];
};
interface Config {
apiUrl: string;
timeout: number;
retries: number;
}
type ReadonlyConfig = ReadonlyProps<Config, "apiUrl">;
泛型函数中的高级约束
复杂的约束可以用于创建高度类型安全的实用函数。
function mergeObjects<T extends object, U extends object>(
obj1: T,
obj2: U
): T & U {
return { ...obj1, ...obj2 };
}
const merged = mergeObjects({ a: 1 }, { b: 2 }); // { a: 1, b: 2 }
类型约束与函数重载
类型约束可以与函数重载结合,为不同输入类型提供精确的类型推断。
function formatInput<T extends string | number>(input: T): string;
function formatInput(input: boolean): "yes" | "no";
function formatInput(input: any): string {
if (typeof input === "boolean") {
return input ? "yes" : "no";
}
return String(input);
}
约束在React组件中的应用
在React组件中,类型约束可以确保props的正确性。
interface BaseProps {
className?: string;
style?: React.CSSProperties;
}
function withCustomProps<T extends BaseProps>(
Component: React.ComponentType<T>
) {
return (props: T) => <Component {...props} />;
}
类型约束与索引签名
约束可以用于限制索引签名的键类型。
type StringMap<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
interface MixedData {
id: string;
count: number;
name: string;
timestamp: Date;
}
type StringOnly = StringMap<MixedData>; // { id: string; name: string }
约束与Promise结合
在异步编程中,约束可以确保Promise解析值的类型。
async function fetchData<T extends { id: string }>(url: string): Promise<T[]> {
const response = await fetch(url);
return response.json();
}
interface Product {
id: string;
name: string;
price: number;
}
fetchData<Product>("/api/products"); // 返回Promise<Product[]>
类型约束的性能考量
虽然类型约束提供了安全性,但过度复杂的约束可能会影响编译性能。对于大型项目,需要平衡类型安全性和编译速度。
// 简单的约束编译更快
function simpleConstraint<T extends number | string>(value: T) {}
// 复杂的约束可能需要更多编译时间
type ComplexConstraint<T> = T extends infer U
? U extends object
? keyof U extends string
? { [K in keyof U]: U[K] }
: never
: U
: never;
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:类型实例化与延迟求值
下一篇:字符串操作类型