TypeScript 核心概念对比
1. const
和 readonly
的区别
特性 | const | readonly |
---|---|---|
应用场景 | 变量声明(阻止重新赋值) | 接口/类型中的属性声明 |
作用范围 | 变量绑定不可变 | 对象属性不可变 |
示例 | const num = 10; | interface User { readonly id: number; } |
引用类型 | 允许修改对象内部属性 | 直接约束属性,编译时检查 |
// const示例
const obj = { name: 'Alice' };
obj.name = 'Bob'; // ✅ 允许修改属性
// obj = {}; // ❌ 不能重新赋值
// readonly示例
interface Config {
readonly apiKey: string;
}
const config: Config = { apiKey: 'xxx' };
// config.apiKey = 'yyy'; // ❌ 只读属性不能修改
2. 枚举(Enum)和常量枚举(Const Enum)的区别
特性 | 枚举(Enum) | 常量枚举(Const Enum) |
---|---|---|
编译结果 | 生成运行时对象 | 编译时内联为值(无运行时代码) |
反向映射 | 支持(Color.Red → 0 和 Color[0] → 'Red' ) | 不支持(仅 Color.Red → 0 ) |
性能 | 有运行时开销 | 无开销(适合高频使用) |
应用场景 | 需要动态访问枚举值 | 纯静态常量(如配置项) |
// 普通枚举
enum Color { Red, Green, Blue }
console.log(Color.Red); // 0
console.log(Color[0]); // 'Red'(反向映射)
// 常量枚举
const enum Direction { Up, Down, Left, Right }
const dir = Direction.Up; // 编译后直接替换为0
3. 接口(Interface)和类型别名(Type Alias)的区别
特性 | 接口(Interface) | 类型别名(Type Alias) |
---|---|---|
定义语法 | interface Point { x: number; } | type Point = { x: number; } |
扩展方式 | extends 关键字 | 交叉类型(& ) |
重复声明 | 自动合并(Declaration Merging) | 报错(不允许重复) |
支持类型 | 仅对象类型(含函数、类) | 任意类型(基本类型、联合类型) |
与类的关系 | 可被类实现(implements ) | 可约束类,但不能被实现 |
// 接口示例
interface Point { x: number; }
interface Point { y: number; } // 合并为 { x: number; y: number; }
// 类型别名示例
type ID = string | number;
type User = { name: string } & Point;
4. any
的作用与风险
作用:
- 表示任意类型,绕过类型检查
- 兼容 JavaScript 遗留代码
- 处理动态数据(如 JSON 解析结果)
风险:
- 丧失类型安全
- 隐藏潜在错误
- 破坏类型系统的价值
let data: any = JSON.parse('{"name": "Alice"}');
data.unknownMethod(); // 编译时不报错,运行时可能崩溃
5. 特殊类型对比表
类型 | 描述 | 典型场景 |
---|---|---|
any | 任意类型,关闭类型检查 | 迁移 JS 代码、动态数据 |
never | 永不存在的值(如抛出异常的函数返回) | 不可能到达的代码路径 |
unknown | 未知类型,使用前需类型断言 | 安全处理不确定类型(替代 any ) |
null | 空值,严格模式下需显式声明(如 string | null ) | 可能为空的变量 |
undefined | 未定义值,严格模式下需显式声明 | 可选参数、未初始化的变量 |
void | 函数无返回值(隐式返回 undefined ) | 回调函数、副作用函数 |
// never示例
function throwError(msg: string): never {
throw new Error(msg);
}
// unknown示例
let input: unknown = getUserInput();
if (typeof input === 'string') {
input.toUpperCase(); // 类型守卫后可用
}
6. Interface 对特殊类型的声明支持
类型 | 支持方式 | 示例代码 |
---|---|---|
函数 | 调用签名(Call Signature) | interface Add { (a: number, b: number): number; } |
数组 | 索引签名(Index Signature) | interface NumberArray { [index: number]: number; } |
类(索引) | 索引签名 + 类实现 | interface Dictionary { [key: string]: string; } |
// 函数接口
interface Comparator {
(a: any, b: any): number;
}
const sortFn: Comparator = (a, b) => a - b;
// 数组接口
interface StringArray {
[index: number]: string;
}
const arr: StringArray = ['a', 'b', 'c'];
// 类索引接口
interface Indexable {
[prop: string]: any;
}
class MyClass implements Indexable {
[key: string]: any;
}
TypeScript 核心概念对比(新增内容)
7. type
和 interface
的深层区别
特性 | 接口(Interface) | 类型别名(Type Alias) |
---|---|---|
扩展方式 | 只能使用 extends 继承其他接口 | 可通过 & 交叉任意类型 |
实现限制 | 可被类 implements | 类只能实现对象类型的 type |
声明合并 | 支持同名接口自动合并 | 重复定义会报错 |
元组支持 | 不支持直接定义元组 | 可定义元组(如 type Tuple = [string, number] ) |
映射类型 | 不支持直接创建映射类型 | 可通过 keyof 创建映射类型 |
// 接口扩展示例
interface Animal { name: string; }
interface Dog extends Animal { bark(): void; }
// 类型别名扩展示例
type Point2D = { x: number; y: number; };
type Point3D = Point2D & { z: number; };
// 元组示例
type Coordinate = [number, number];
const coord: Coordinate = [10, 20];
8. declare
的作用
用途:
- 声明全局变量、函数、类等,不产生实际代码
- 告诉 TypeScript “这个东西在其他地方已经定义了”
- 主要用于编写类型声明文件(
.d.ts
)
常见场景:
- 声明外部库的类型
- 扩展全局命名空间
- 声明 DOM API
// 声明全局变量
declare const process: {
env: {
NODE_ENV: 'development' | 'production';
};
};
// 使用全局变量(无需导入)
console.log(process.env.NODE_ENV);
// 声明全局函数
declare function greet(name: string): void;
greet('Alice'); // 无需实现,只需类型声明
9. declare global
的作用
用途:
- 在模块内部扩展全局命名空间
- 为全局对象(如
Window
、Array
)添加自定义属性或方法
示例:
// 扩展全局 Window 对象
declare global {
interface Window {
myCustomProp: string; // 为 window 添加自定义属性
myCustomMethod(): void; // 为 window 添加自定义方法
}
}
// 使用扩展后的全局对象
window.myCustomProp = 'Hello';
window.myCustomMethod = () => console.log('Custom method');
扩展内置类型:
// 为 Array 增加自定义方法
declare global {
interface Array<T> {
first(): T | undefined; // 返回数组第一个元素
}
}
// 实现扩展方法
Array.prototype.first = function() {
return this.length > 0 ? this[0] : undefined;
};
// 使用扩展方法
const arr = [1, 2, 3];
console.log(arr.first()); // 1
10. keyof
关键字的作用
用途:
- 获取类型的所有公共属性名组成的联合类型
- 常用于泛型约束、索引类型查询
语法:
type Keys = keyof T; // T 为任意类型
示例:
interface User {
name: string;
age: number;
email: string;
}
type UserKeys = keyof User; // 等同于 'name' | 'age' | 'email'
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key]; // 安全访问属性
}
const user: User = { name: 'Alice', age: 30, email: 'a@b.com' };
const name = getProperty(user, 'name'); // 类型为 string
// const invalid = getProperty(user, 'invalidKey'); // 报错:类型 '"invalidKey"' 不能赋给类型 'keyof User'
对基本类型使用 keyof
:
type NumberKeys = keyof number; // 'toString' | 'toFixed' | ... 等数字原型上的方法
type StringKeys = keyof string; // 'charAt' | 'substring' | ... 等字符串原型上的方法
11. typeof
关键字的作用
用途:
- 在类型上下文中获取变量或属性的类型
- 与 JavaScript 的
typeof
运算符不同(后者返回值为字符串)
语法:
type Type = typeof value; // value 为任意变量或表达式
示例:
const user = {
name: 'Alice',
age: 30,
isAdmin: true
};
type UserType = typeof user; // 等同于 { name: string; age: number; isAdmin: boolean; }
const anotherUser: UserType = {
name: 'Bob',
age: 25,
isAdmin: false
};
结合 keyof
使用:
const config = {
apiKey: 'xxx',
timeout: 5000,
retry: true
};
type ConfigKeys = keyof typeof config; // 'apiKey' | 'timeout' | 'retry'
获取函数返回值类型:
function createUser() {
return {
id: 1,
name: 'Alice',
createdAt: new Date()
};
}
type UserReturnType = typeof createUser extends () => infer R ? R : never;
// 等同于 { id: number; name: string; createdAt: Date; }
12. keyof
vs typeof
对比表
关键字 | 作用 | 应用场景 |
---|---|---|
keyof | 获取类型的所有属性名组成的联合类型 | 泛型约束、属性访问安全校验 |
typeof | 获取变量或表达式的类型 | 类型推导、类型复用 |
组合使用示例:
// 获取对象属性值的类型
const settings = {
darkMode: true,
fontSize: 16,
theme: 'light'
};
type SettingValues = typeof settings[keyof typeof settings];
// 等同于 boolean | number | string
最佳实践建议(新增)
-
keyof
的应用场景:- 实现通用的属性访问函数(如
getProperty
示例) - 约束泛型参数为对象的属性名
- 实现类型安全的
pick
、omit
等工具函数
- 实现通用的属性访问函数(如
-
typeof
的应用场景:- 从现有变量推导类型,避免重复定义
- 与
keyof
结合获取对象属性名和属性值类型 - 获取函数返回值类型(替代手动书写类型)
-
组合使用示例:
// 类型安全的属性选择器
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
const result = {} as Pick<T, K>;
keys.forEach(key => {
result[key] = obj[key];
});
return result;
}
const user = { name: 'Alice', age: 30, email: 'a@b.com' };
const picked = pick(user, ['name', 'age']); // 类型为 { name: string; age: number; }