别再死记硬背了!用几个真实代码片段,帮你彻底搞懂TypeScript的interface和type
别再死记硬背了!用几个真实代码片段,帮你彻底搞懂TypeScript的interface和type
刚接触TypeScript时,interface和type这两个概念就像一对双胞胎,让人傻傻分不清楚。官方文档的抽象解释看了一遍又一遍,可一到实际项目中还是不知道该用哪个。今天我们就用几个真实项目中的代码片段,通过"改代码看效果"的方式,让你在动手实践中彻底掌握它们的区别。
1. 从Ant Design Vue看interface的声明合并
打开Ant Design Vue的源码,你会发现大量interface的使用场景。比如在 button.ts 中,Button组件的Props定义是这样的:
export interface ButtonProps {
size?: 'large' | 'default' | 'small';
type?: 'primary' | 'ghost' | 'dashed' | 'link' | 'text';
}
export interface ButtonProps {
loading?: boolean | { delay?: number };
disabled?: boolean;
}
动手实验 :
- 尝试把第二个
ButtonProps改为type定义 - 观察TypeScript给出的错误提示
你会发现,使用 type 时会报错:"Duplicate identifier 'ButtonProps'"。这就是 interface 独有的 声明合并 特性——同名的interface会自动合并,而type不允许重复定义。
实际应用场景 :在大型项目中,当不同模块需要扩展同一个类型定义时,interface的这种特性就非常有用。比如:
// user模块定义基础用户类型
interface User {
name: string;
age: number;
}
// admin模块扩展用户类型
interface User {
permissions: string[];
}
2. API响应处理:type的联合类型优势
处理API响应时,我们经常需要表示"成功或失败"的联合类型。看看这个真实场景:
type ApiResponse<T> =
| { status: 'success'; data: T; timestamp: number }
| { status: 'error'; message: string; code: number };
function handleResponse(response: ApiResponse<User>) {
if (response.status === 'success') {
console.log(response.data); // 类型安全!
} else {
console.error(response.message);
}
}
为什么这里用type?
- interface无法直接表示"或"的关系
- 联合类型配合类型守卫(type guard),能实现完美的类型收窄
进阶技巧 :当需要基于现有类型创建新类型时,type的条件类型特别强大:
type Nullable<T> = T | null;
type Promisify<T> = Promise<T>;
3. 组件Props定义:interface vs type实战对比
在React/Vue组件开发中,Props的类型定义既可以用interface也可以用type。让我们对比两种写法:
// 方案A: interface
interface ModalProps {
visible: boolean;
title?: string;
onClose: () => void;
}
// 方案B: type
type ModalProps = {
visible: boolean;
title?: string;
onClose: () => void;
};
看似相同,实则差异 :
- interface更适合扩展:
interface FullScreenModalProps extends ModalProps { fullScreen: boolean; } - type更适合复杂组合:
type Theme = 'light' | 'dark'; type ThemedModalProps = ModalProps & { theme: Theme; };
团队规范建议 :如果组件库需要频繁扩展,优先使用interface;如果需要大量类型运算,优先使用type。
4. 高级类型体操:type的独门绝技
当我们需要进行复杂类型操作时,type展现出不可替代的优势。看这个从axios源码简化而来的例子:
type Method = 'get' | 'post' | 'put' | 'delete';
type RequestConfig<M extends Method> = {
method: M;
url: string;
} & (M extends 'get' | 'delete' ? { params: any } : { data: any });
function request<M extends Method>(config: RequestConfig<M>) {
// 实现...
}
// 使用时类型检查非常精确
request({ method: 'get', url: '/api', params: {} }); // ✅
request({ method: 'post', url: '/api', data: {} }); // ✅
request({ method: 'get', url: '/api', data: {} }); // ❌ 错误!
关键点解析 :
- 使用条件类型实现方法相关的参数校验
- interface无法实现这种动态类型计算
- 这种模式在复杂工具库中非常常见
另一个实用技巧 :模板字面量类型
type EventName = 'click' | 'hover';
type HandlerName = `on${Capitalize<EventName>}`;
// 结果是: 'onClick' | 'onHover'
5. 性能与最佳实践
关于interface和type的性能差异,社区有一些误解。通过实测可以得出:
内存占用 :
- 简单类型:无显著差异
- 大型项目:interface可能略优,因为类型检查器会缓存接口声明
编译速度 :
- 小型项目:无感
- 超大型项目:复杂type可能稍慢
实际建议 :
- 保持一致性:团队内部统一规范
- 按需选择:
- 需要声明合并 → interface
- 需要类型运算 → type
- 不必过度优化:除非性能确实成为瓶颈
在Vue 3源码中,我们可以看到两者混合使用的典范:
// 基础形状用interface
interface ComponentOptions {
// ...
}
// 复杂类型运算用type
type ComponentPublicInstance = /* 复杂的类型运算 */;
更多推荐
所有评论(0)