Skip to content

TypeScript 的核心就是类型系统——它给 JavaScript 加了静态类型,让很多 bug 在编译期就暴露。平时写 Vue/React 天天用,但"interface 和 type 到底啥区别、联合/交叉类型、类型守卫"这些核心概念一直没系统梳理。这篇把 TS 类型系统最核心的几块讲清楚。

一、解决什么问题:JS 的弱类型之痛

JavaScript 是动态弱类型,变量类型运行时才确定,容易写出"数字 + 字符串 = 意外结果"这类 bug,而且 IDE 提示也弱。

TypeScript 加静态类型:变量声明时就确定类型,编译期就能查出类型错误:

typescript
// JS:类型自由,容易出错
let name = "BinMaker";
name = 18;          // JS 允许,运行时可能出错

// TS:类型注解,编译期报错
let name: string = "BinMaker";
name = 18;          // ❌ 编译报错:不能把 number 赋给 string

好处:bug 从运行时提前到编译期,IDE 有完整的类型提示和补全。

二、基础类型与类型注解

typescript
let name: string = "BinMaker";     // 字符串
let age: number = 18;              // 数字
let isVip: boolean = true;         // 布尔
let tags: string[] = ["a", "b"];   // 字符串数组
let tuple: [string, number] = ["x", 1];   // 元组
let anything: any = "随便";        // any:放弃类型检查(尽量少用)
let nothing: null = null;

any 是"逃生门"——用了它等于放弃类型检查,能不用就不用。

三、interface vs type(面试高频)

两者都能定义对象类型,但语义和用法有区别:

typescript
// interface:接口,可扩展、可声明合并
interface User {
    name: string;
    age: number;
}
interface Admin extends User {    // 可 extends 扩展
    role: string;
}

// type:类型别名,能表达更复杂的类型
type User = {
    name: string;
    age: number;
};
type ID = string | number;        // type 能表示联合类型,interface 不能
维度interfacetype
扩展extends 继承& 交叉
联合类型不能能(string | number
声明合并能(同名 interface 自动合并)不能
适用对象/类的结构(约定"形状")复杂类型(联合/交叉/映射)

选型口诀:定义"对象的形状"优先 interface(可扩展),表达"复杂类型组合"(联合、交叉、映射)用 type

四、联合类型与交叉类型

联合类型(|:是 A 或 B,二选一:

typescript
type ID = string | number;          // 可以是 string 或 number
function printId(id: ID) {
    console.log(id);                // id 是 string | number
}

交叉类型(&:是 A 且 B,合并:

typescript
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged;         // 同时有 name 和 age
const p: Person = { name: "BinMaker", age: 18 };

一句话| 是"或"(满足一个就行),& 是"且"(两个都要有)。

五、类型守卫:收窄类型

联合类型不能直接用特定类型的方法,要用类型守卫收窄:

typescript
// ① typeof:收窄基础类型(string/number/boolean...)
function getLen(x: string | number) {
    if (typeof x === "string") {
        return x.length;    // 收窄为 string,能用 length
    }
    return x;               // 收窄为 number
}

// ② instanceof:判断是否是某个类的实例
class Cat { meow() {} }
class Dog { bark() {} }
function makeSound(animal: Cat | Dog) {
    if (animal instanceof Cat) {
        return animal.meow();   // 收窄为 Cat,能调 meow
    }
    return animal.bark();       // 收窄为 Dog,能调 bark
}

// ③ in:判断对象是否有某个属性(判别联合类型)
type Circle = { kind: "circle"; radius: number };
type Square = { kind: "square"; side: number };
function getArea(shape: Circle | Square) {
    if ("radius" in shape) {
        return Math.PI * shape.radius ** 2;   // 收窄为 Circle
    }
    return shape.side ** 2;                    // 收窄为 Square
}

// ④ is:自定义类型守卫(返回类型谓词 obj is User)
function isUser(obj: any): obj is User {
    return obj && typeof obj.name === "string";
}
if (isUser(data)) {
    data.name;   // data 收窄为 User,能用 name
}

四种类型守卫各管一类:typeof 收窄基础类型,instanceof 收窄类实例,in 判别对象形状(联合类型),is 自定义判断逻辑。

类型守卫(typeofinstanceofinis)让 TS 在判断分支里"知道"变量的具体类型,从而安全地调用方法。

小结

  • TS 用静态类型把 bug 从运行时提前到编译期
  • interface 定义对象形状(可扩展),type 表达复杂类型(联合/交叉)
  • 联合 | = 或,交叉 & = 且
  • 类型守卫(typeof/instanceof/in/is)收窄联合类型,安全调用方法
  • any 是逃生门,尽量少用

类型系统里还有个最核心、也最考验功力的概念——泛型,接着看《TypeScript 泛型》。