TypeScript 类型系统基础
更新时间:2026-08-29。本文回答:TS 的接口和 Java 接口有何不同?联合类型和枚举怎么选?类型收窄到底是什么?
一、基础类型与注解
TS 在 JS 类型之上加了编译期注解:
ts
let age: number = 30;
let name: string = "tom";
let ok: boolean = true;
let list: number[] = [1, 2, 3];
let tuple: [string, number] = ["cpu", 4];any表示"绕过检查",应尽量避免(用unknown替代更安全)。unknown是"类型未知但安全"——使用前必须先收窄(narrowing)。
二、接口 vs 类型别名
描述对象形状有两种写法:
ts
interface User { id: number; name: string }
type Point = { x: number; y: number };| 维度 | interface | type |
|---|---|---|
| 扩展 | extends 继承、可声明合并 | 用 & 交叉 |
| 联合/映射 | 不支持 | 支持 |
| 适合 | 对象/类契约 | 联合、元组、工具类型 |
结构化类型(鸭子类型):TS 不要求显式 implements,只要形状匹配即可赋值——和 Java 的 nominal(名义)类型相反。
ts
interface Shape { area(): number }
class Circle { area() { return 3.14 } } // 没声明 implements Shape
const s: Shape = new Circle(); // 形状匹配即合法三、联合与字面量类型
ts
type Direction = "up" | "down" | "left" | "right";
type Result<T> = { ok: true; value: T } | { ok: false; error: string };- 字面量类型把"具体值"当类型,配合联合可精确描述有限集合。
- 可辨识联合(discriminated union):用一个公共字段(如
ok)区分分支,配合收窄很安全。
四、类型收窄(Narrowing)
TS 会根据控制流自动"收窄"变量类型:
ts
function len(x: string | string[]): number {
if (typeof x === "string") {
return x.length; // 这里 x 是 string
}
return x.length; // 这里 x 是 string[]
}常见收窄手段:
typeof/instanceof判断in操作符判断属性- 自定义类型守卫
x is T - 可辨识联合的
switch分支
五、与其他语言的对比
| 概念 | TypeScript | Rust | Java |
|---|---|---|---|
| 接口 | 结构化(形状匹配) | trait(需 impl) | 名义(显式 implements) |
| 联合 | 原生 ` | ` | enum/模式匹配 |
| 类型时机 | 编译期擦除 | 编译期保留 | 编译期(泛型擦除) |
| 类型守卫 | x is T | match 模式 | instanceof |
和 Rust trait 相比,TS 的接口更"宽松"(不强制 impl),代价是运行时没有类型保证。
六、常见坑
any扩散:一旦用any,类型信息会沿调用链丢失,优先unknown+ 收窄。- 接口声明合并的副作用:同名
interface会自动合并,大型项目里容易意外叠加字段。 null/undefined:默认不在类型里,开strictNullChecks后才能强制处理空值(强烈建议开启)。
一句话总结
TS 类型系统 = 结构化接口(形状即契约)+ 联合/字面量精确建模 + 控制流自动收窄——把"值的可能形态"在编译期刻画清楚,错误在写代码时就暴露。