前言
在代码的世界里,有一句话曾被无数开发者奉为圭臬:
“强类型可以杜绝 98% 的类型错误。”
TypeScript(下文简称 TS)的理解还停留在“会报错的 JavaScript”阶段。甚至有人在笔记里写道:“TS 是 JS 的超级”。
其实,准确的说法是:TS 是 JS 的“超集”(Superset)。
这意味着 TS 包含了 JS 的所有特性,并在此基础上像钢铁侠穿上战甲一样,增加了静态类型系统。TS 最终会被编译成 JS 在浏览器运行,但它能让你在代码运行前(编写阶段)就发现那些可能导致崩溃的 Bug。
一、 基础类型:告别“裸奔”的变量
在 JS 中,变量可以随意改变类型,这虽然灵活,但也是 Bug 的温床。TS 的第一步,就是给变量穿上“约束衣”。
TypeScript
// 基础类型注解
let b: string = 'hello';
let c: boolean = true;
let d: null = null;
let e: undefined = undefined;
// 数组的两种定义方式
let arr: number[] = [1, 2, 3, 4, 5]; // 方式一:类型[]
let arr2: Array<string> = ['a', 'b', 'c']; // 方式二:泛型 Array<T>
// 元组 (Tuple):一种特殊的数组,限定了长度和每个位置的类型
// let user = [1, 'Tom']; // 这样写 TS 会推导为 (string|number)[]
let user: [number, string] = [1, 'Tom']; // 这是元组,主要用于 CSV 解析或 React Hooks 返回值
提示:在实际开发中,如果变量有初始值,TS 会自动进行类型推导(Type Inference)。例如 let a = 18,TS 自动知道它是 number。不要给每个变量都写类型注解,那样代码会很啰嗦。
二、 枚举(Enum):拒绝“魔术数字”
在业务开发中,我们经常遇到状态判断,比如订单状态 0、1、2。如果直接写数字,代码很难维护(谁记得 2 代表什么?)。
TS 借鉴了 Java/C# 等语言,引入了 枚举。 TypeScript
enum Status {
Pending, // 默认为 0
Success, // 1
Failed, // 2
}
let s: Status = Status.Pending;
if (s === Status.Success) {
// 逻辑清晰,可读性极高
}
划重点:枚举不仅增强了可读性,还是 TS 中少数会产生运行时代码的特性之一(稍后部分细说)。
三、 Any vs Unknown:放纵与克制的艺术
这是很多新手的重灾区,也是面试必考点。
1. Any:放弃治疗的救命稻草
TypeScript
// ts初学的时候 any救命
let aa: any = 1; // 任意类型,放弃类型约束
aa = "11";
aa = {};
aa = true;
// 甚至可以调用不存在的方法,编译时不报错,运行时崩溃
// aa.fly();
当你把类型设为 any,你就把 TS 降级回了 JS。虽然写起来爽,但可能会导致严重的生产事故。大厂项目中,通常会在 ESLint 中禁用显式的 any。
2. Unknown:安全的防火墙
TS 3.0 引入了 unknown,它是 any 的安全版。
TypeScript
let bb: unknown = 1; // 未知类型
bb = 'b'; // 可以赋值任何类型
// bb.hello(); // 报错!对象是未知类型,不能随意调用方法
为什么 unknown 更安全?
因为它强制你在使用变量之前,必须进行类型检查(Type Narrowing)。
TypeScript
if (typeof bb === 'string') {
console.log(bb.toUpperCase()); // TS 知道现在它是 string 了
}
四、 接口 (Interface) vs 类型别名 (Type)
这是定义对象结构的两种主要方式。
TypeScript
// --- Interface 方式 ---
interface User {
name: string;
age: number;
readonly id: number; // 只读属性,初始化后不可修改
hobby?: string; // 可选属性,可传可不传
}
const u: User = {
name: 'Tom',
age: 18,
id: 1001,
hobby: 'reading',
}
// u.id = 1002; // 报错:id 是只读的
// --- Type Alias 方式 ---
type ID = string | number; // 联合类型,这是 Type 的强项
type UserType = {
name: string;
age: number;
id: ID, // 复用上面定义的联合类型
hobby?: string
}
很多同学会问:它俩到底用哪个?
| 特性 | Interface | Type | |
|---|---|---|---|
| 定义对象/函数 | 支持 | 支持 | |
| 声明合并 (同名合并) | 支持 (适合库开发者) | 不支持 (报错) | |
| 联合类型/元组 | 不支持 | 支持 (type A = string | number) |
| 扩展方式 | extends | & (交叉类型) |
大厂最佳实践:
- 编写库(Library)或插件时,优先用 interface,因为方便使用者扩展。
- 定义联合类型、复杂的工具类型转换时,必须用 type。
- 普通业务代码中,保持团队风格统一即可(目前 React 社区偏向用 type 略多一点)。
结语
回到文章开头的那段代码,看到 // ts初学的时候 any救命
从 JavaScript 的“随心所欲”到 TypeScript 的“循规蹈矩”,初学者一定会感到痛苦和束缚。但请相信我,当你维护一个超过 5 万行代码的大型项目时,那些红色的波浪线报错,不再是阻碍你的绊脚石,而是防止你跌入深渊的护栏。
TS 不仅是技术,更是一种严谨的工程思维。