type、interface 与泛型
type 和 interface 的区别
两者都能描述对象,并且多数场景可以互换。
interface User {
id: number;
name: string;
}
type UserWithRole = User & {
role: 'admin' | 'member';
};
主要差异:
| 能力 | interface | type |
|---|
| 描述对象、函数 | 支持 | 支持 |
| 继承/组合 | extends | 交叉类型 & |
| 联合类型 | 不支持 | 支持 |
| 声明合并 | 支持 | 不支持 |
| 映射、条件类型 | 不直接支持 | 支持 |
选择建议:
- 对外暴露、可能被扩展的对象契约优先
interface。
- 联合类型、工具类型和复杂组合优先
type。
- 团队一致性比机械坚持某一种更重要。
泛型解决什么问题
泛型让类型之间的关系由调用方决定,同时保留推导能力。
function first<T>(items: T[]): T | undefined {
return items[0];
}
const name = first(['A', 'B']); // string | undefined
不用泛型而返回 any 会丢失输入与输出的关系。
泛型约束
function getProperty<T, K extends keyof T>(object: T, key: K): T[K] {
return object[key];
}
const user = { id: 1, name: 'Ada' };
getProperty(user, 'name'); // string
// getProperty(user, 'age'); // 编译错误
这里的关键关系是:K 必须是 T 的键,返回值则是对应属性的类型。
keyof、typeof 与索引访问类型
const config = {
theme: 'dark',
pageSize: 20,
} as const;
type Config = typeof config;
type ConfigKey = keyof Config; // 'theme' | 'pageSize'
type Theme = Config['theme']; // 'dark'
注意 TypeScript 的 typeof 出现在类型位置时用于获取变量的静态类型,不等同于运行时 typeof 运算符。
组件泛型示例
type SelectProps<T> = {
options: T[];
getKey: (item: T) => string;
renderLabel: (item: T) => string;
onChange: (item: T) => void;
};
function createSelect<T>(props: SelectProps<T>) {
return props.options.map((item) => ({
key: props.getKey(item),
label: props.renderLabel(item),
}));
}
设计泛型 API 时,应让 TypeScript 尽量从参数推导类型,避免要求调用者重复书写泛型参数。