泛型接口
泛型接口的基本概念
泛型接口允许我们创建可重用的组件,这些组件可以处理多种类型而不是单一类型。TypeScript中的泛型接口通过在接口定义中使用类型参数来实现这一点。类型参数在接口名称后面的尖括号中声明,可以在接口的属性和方法中使用。
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
泛型接口的语法结构
泛型接口的语法与普通接口类似,但在接口名称后添加了类型参数。类型参数可以是任何有效的标识符,通常使用单个大写字母如T、U、V等。这些类型参数可以在接口的各个部分使用,包括属性类型、方法参数类型和返回类型。
interface KeyValuePair<K, V> {
key: K;
value: V;
}
let pair1: KeyValuePair<number, string> = { key: 1, value: "one" };
let pair2: KeyValuePair<string, boolean> = { key: "isValid", value: true };
泛型接口与函数
泛型接口常用于描述函数类型,这使得我们可以创建灵活的函数签名。函数泛型接口可以指定参数类型和返回类型之间的关系,同时保持类型安全。
interface Transformer<T, U> {
(input: T): U;
}
const stringToNumber: Transformer<string, number> = (str) => parseFloat(str);
const numberToString: Transformer<number, string> = (num) => num.toString();
泛型接口与类
类可以实现泛型接口,这要求类也必须成为泛型类或者为接口的类型参数提供具体类型。这种模式在创建可重用组件时特别有用。
interface Repository<T> {
add(item: T): void;
get(id: number): T;
getAll(): T[];
}
class UserRepository implements Repository<User> {
private users: User[] = [];
add(user: User): void {
this.users.push(user);
}
get(id: number): User {
return this.users.find(user => user.id === id)!;
}
getAll(): User[] {
return [...this.users];
}
}
泛型接口的约束
我们可以通过extends关键字对泛型参数添加约束,限制可以使用的类型。这确保了类型参数具有特定的属性或方法,同时仍然保持灵活性。
interface Lengthwise {
length: number;
}
interface SizedContainer<T extends Lengthwise> {
contents: T;
size(): number;
}
const stringContainer: SizedContainer<string> = {
contents: "hello",
size() {
return this.contents.length;
}
};
泛型接口的默认类型
TypeScript允许为泛型参数指定默认类型,当使用接口时没有显式提供类型参数时,将使用默认类型。
interface PaginatedResponse<T = any> {
data: T[];
total: number;
page: number;
perPage: number;
}
const userResponse: PaginatedResponse<User> = {
data: [{ id: 1, name: "Alice" }],
total: 1,
page: 1,
perPage: 10
};
const anyResponse: PaginatedResponse = {
data: [1, 2, 3],
total: 3,
page: 1,
perPage: 10
};
泛型接口与索引签名
泛型接口可以与索引签名结合使用,创建灵活的字典或映射类型。这种模式在处理动态键值对时特别有用。
interface Dictionary<T> {
[key: string]: T;
}
const numberDict: Dictionary<number> = {
"one": 1,
"two": 2,
"three": 3
};
const stringDict: Dictionary<string> = {
"en": "English",
"es": "Spanish",
"fr": "French"
};
泛型接口的高级用法
泛型接口可以嵌套使用,创建更复杂的类型结构。这种模式在构建大型应用程序或库时特别有价值。
interface ApiResponse<T> {
success: boolean;
data?: T;
error?: string;
}
interface UserProfile {
id: number;
name: string;
email: string;
}
interface PaginatedApiResponse<T> extends ApiResponse<T[]> {
page: number;
totalPages: number;
}
const userResponse: ApiResponse<UserProfile> = {
success: true,
data: {
id: 1,
name: "John Doe",
email: "john@example.com"
}
};
const usersResponse: PaginatedApiResponse<UserProfile> = {
success: true,
data: [
{ id: 1, name: "John Doe", email: "john@example.com" },
{ id: 2, name: "Jane Smith", email: "jane@example.com" }
],
page: 1,
totalPages: 3
};
泛型接口与条件类型
在TypeScript的高级类型系统中,泛型接口可以与条件类型结合使用,根据类型参数创建不同的类型结构。
type CheckType<T> = T extends string ? "string" : "not string";
interface TypeChecker<T> {
type: CheckType<T>;
value: T;
}
const stringChecker: TypeChecker<string> = {
type: "string",
value: "hello"
};
const numberChecker: TypeChecker<number> = {
type: "not string",
value: 42
};
泛型接口与映射类型
映射类型可以与泛型接口结合,动态地转换现有类型的属性。这种技术在创建实用工具类型时特别有用。
interface ReadonlyWrapper<T> {
readonly [P in keyof T]: T[P];
}
interface User {
id: number;
name: string;
email: string;
}
const readonlyUser: ReadonlyWrapper<User> = {
id: 1,
name: "Alice",
email: "alice@example.com"
};
// readonlyUser.name = "Bob"; // 错误: 无法分配到 "name" ,因为它是只读属性
泛型接口在实际项目中的应用
在实际项目中,泛型接口常用于API响应处理、状态管理、数据访问层等场景。它们提供了类型安全的同时保持了代码的灵活性。
// API响应处理
interface ApiResponse<T> {
status: number;
data: T;
message?: string;
}
// 状态管理
interface State<T> {
loading: boolean;
error: string | null;
data: T | null;
}
// 数据访问层
interface DataAccess<T, ID> {
create(item: T): Promise<T>;
read(id: ID): Promise<T | null>;
update(id: ID, item: T): Promise<boolean>;
delete(id: ID): Promise<boolean>;
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn