1. TypeScript中的引用与const常量解析
在TypeScript开发中,引用类型和const常量的使用看似基础,却藏着不少容易踩坑的细节。作为从ES6时代就开始使用TypeScript的老手,我见过太多因为不理解引用本质而导致的bug。今天我们就来彻底拆解这个主题,不仅讲清楚概念,更分享实际项目中的经验教训。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 引用类型的内存机制
2.1 堆栈模型中的引用行为
TypeScript中的对象、数组等引用类型,其实际值存储在堆内存中,而变量保存的只是指向堆内存地址的引用。这导致了一个经典问题:
typescript复制const users = [{ name: 'Alice' }];
const adminList = users;
adminList[0].name = 'Bob';
console.log(users[0].name); // 输出'Bob'!
警告:很多开发者误以为const能保护对象内容不被修改,其实它只保证引用地址不变
2.2 深度冻结的实战方案
在需要真正不可变数据的场景(如Redux的state),我们可以使用深度冻结:
typescript复制function deepFreeze<T>(obj: T): Readonly<T> {
Object.freeze(obj);
Object.getOwnPropertyNames(obj).forEach(prop => {
if (obj[prop] !== null
&& typeof obj[prop] === 'object'
&& !Object.isFrozen(obj[prop])) {
deepFreeze(obj[prop]);
}
});
return obj;
}
const config = deepFreeze({
api: { endpoint: 'https://api.example.com' }
});
// config.api.endpoint = '' // 编译和运行时都会报错
3. const常量的类型推导
3.1 字面量类型窄化
const声明的原始类型变量会被自动推导为字面量类型:
typescript复制const MAX_SIZE = 10; // 类型是10,而不是number
const BRAND = 'Apple'; // 类型是'Apple',而不是string
这在函数重载和类型守卫中特别有用:
typescript复制function handleResponse(status: 200 | 404) {
// ...
}
const resStatus = 200; // 类型是200
handleResponse(resStatus); // 完美匹配
3.2 对象常量的问题与解决
但对象常量不会自动窄化:
typescript复制const defaultConfig = {
timeout: 5000, // 类型是number
retry: false // 类型是boolean
};
解决方案是使用as const断言:
typescript复制const defaultConfig = {
timeout: 5000,
retry: false
} as const;
// 现在timeout类型是5000,retry类型是false
4. 引用与const的工程实践
4.1 不可变数据模式
在大型项目中,我推荐使用immer处理嵌套更新:
typescript复制import produce from 'immer';
const state = {
user: {
name: 'Alice',
permissions: ['read']
}
};
const newState = produce(state, draft => {
draft.user.per
