写在前面
灵活不等于可靠。
为什么要从JavaScript到TypeScript TypeScript 并不是要你放弃 JavaScript 的表达力,而是给你一个"安全网"。它不会阻止你写代码,但会在你差点掉坑时轻轻拉你一把。
下面是我踩过坑后总结的一些核心认知,希望能帮你少走弯路。
一、类型
很多人抗拒 TS,是因为觉得"多写了好多冒号和尖括号"。但其实,类型最大的价值不在编译器,而在人与人之间的沟通。
想象一下:
你写了一个工具函数,其他人调用时不用翻源码、不用问你:"第二个参数到底传对象还是数组?"
半年后你自己回看代码,看到 user: UserProfile,立刻知道它有哪些字段,不用猜。
类型就是一种无需注释的文档。而且,它永远不会过期------因为一旦结构变了,类型不匹配,代码根本跑不起来。
所以,别把类型当成负担,把它当作你和团队、和未来的自己之间的约定。

二、先搞定这些基础
1. 基本类型
ts
const userId: number = 123;
const username: string = "lin";
const isAdmin: boolean = false;
虽然 TS 能自动推断,但在函数参数、返回值、配置项等"接口位置",显式标注是值得的 。尤其是 boolean,JS 里 !!"0" 是 true,这种隐式转换太容易误判。 
2. 数组 ≠ 元组
number[]:一堆数字,顺序不重要; [string, number]:第一个必须是字符串,第二个必须是数字,长度固定。
比如登录成功后返回 [token, expireTime],用元组比返回一个普通数组更明确。 
3. 枚举
老派写法:
ts
enum Role { Admin, User }
现代写法:
ts
type Role = 'admin' | 'user';
后者没有运行时开销,打包后直接变成字符串,还能和后端 JSON 完美对齐。除非你真的需要数值映射,否则优先用字面量。 
3. 外部数据进来
从 API、localStorage、URL 参数拿到的数据,永远不要相信它的类型。
请你别这么干:
ts
const user: any = JSON.parse(localStorage.getItem('user'));
console.log(user.name); // 如果 user 是 null?崩了。
正确姿势:
ts
const raw = localStorage.getItem('user');
if (typeof raw === 'string') {
try {
const user = JSON.parse(raw);
if (user && typeof user.name === 'string') {
// 现在可以安全使用
}
} catch {}
}
或者更优雅地,写个类型守卫函数:
ts
function isUser(obj: unknown): obj is User {
return (
typeof obj === 'object' &&
obj !== null &&
'name' in obj &&
typeof (obj as any).name === 'string'
);
}
记住:所有外部输入都是 unknown,直到你验证过它。
4. interface 和 type
这是新手最容易纠结的问题。其实很简单:
用 interface 描述"东西长什么样"
比如用户信息、API 返回结构、组件 props。它天然支持扩展和合并,适合做"契约"。
用 type 做"类型运算"
比如:
ts
type Nullable<T> = T | null;
type ReadonlyUser = Readonly<User>;
type Status = 'idle' | 'loading';
如果你只是定义一个对象结构,两者几乎等价。但一旦涉及联合、交叉、映射,type 更强大。
我的习惯:对外暴露的结构用 interface,内部工具类型用 type 。 
5. 泛型
很多人觉得泛型很"高级",其实它解决的是一个朴素问题:如何写出既通用又安全的代码?
举个真实场景:封装请求方法。
坏写法(返回 any):
ts
function request(url) {
return fetch(url).then(res => res.json());
}
好写法(带泛型):
ts
async function request<T>(url: string): Promise<T> {
const res = await fetch(url);
return res.json();
}
// 使用
const user = await request<User>('/api/me');
这里 T 就像一个"占位符",由调用者决定它是什么。TS 会确保后续所有操作都基于这个类型。
再比如,你想写一个缓存函数:
ts
const cache = new Map<string, unknown>();
function get<T>(key: string, fallback: T): T {
const val = cache.get(key);
return val !== undefined ? (val as T) : fallback;
}
注意:这里用了类型断言 (val as T),前提是你要确保存进去的时候类型一致 。更好的做法是让 set 也带泛型,并配对使用。
6. 写 TS 的三个心法
边界清晰,内部自由
函数签名、公共 API、跨模块数据------这些地方类型要严;函数内部临时变量,靠推断就行。
宁可报错,不可沉默
遇到不确定的类型,宁愿让它报错,也不要随手写个 any。 unknown + 类型守卫 是更负责任的做法。
先想结构,再写逻辑
动手前花两分钟定义好核心数据模型(比如 Book, BorrowRecord),后面写业务逻辑会顺畅很多,bug 也会少很多。
总结
TypeScript 不会让你变成更好的程序员,但它能放大你已有的工程素养。
如果你本来就有良好的抽象能力、清晰的模块划分、严谨的边界意识,TS 会让你如虎添翼; 如果你习惯"先跑起来再说",那 TS 可能一开始会让你觉得"处处受限"------但那恰恰是你需要成长的地方。
别怕类型系统,它不是来限制你的,而是来替你记住那些你不想再记的细节。
现在,打开你的编辑器,给那个最常被别人问"参数怎么传"的函数,加上类型吧。你会发现,世界突然安静了。