1. 基本类型
TypeScript 的基本类型与你熟悉的语言类似,但全部使用小写:
// 显式类型注解
let name: string = "TypeScript";
let age: number = 12; // 没有 int/float 之分,统一为 number
let isDone: boolean = false;
let big: bigint = 100n; // 大整数,注意后缀 n
let id: symbol = Symbol("id"); // 唯一标识符
💡 类型推断(Type Inference):TypeScript 能自动推断变量类型,不需要总是手写注解。
// 类型推断 —— 编译器自动推导类型
let city = "Shanghai"; // 推断为 string
let count = 42; // 推断为 number
let active = true; // 推断为 boolean
// const 声明会推断为字面量类型
const PI = 3.14; // 推断为 3.14,不是 number
const lang = "TS"; // 推断为 "TS",不是 string
🔄 与 Java 的区别
TS 的类型是结构化的(structural typing),不是名义的(nominal typing)。两个结构相同的类型是兼容的,不需要显式声明 implements。这被称为"鸭子类型"——如果它走起来像鸭子、叫起来像鸭子,那它就是鸭子。
2. 特殊类型
// any —— 逃生舱口,关闭类型检查(尽量避免!)
let anything: any = 42;
anything = "hello"; // 不报错
anything.foo.bar; // 不报错,但运行时可能崩溃
// unknown —— 安全的 any,使用前必须检查类型
let data: unknown = fetchSomething();
// data.toString(); // ❌ 编译错误!不能直接使用
if (typeof data === "string") {
data.toUpperCase(); // ✅ 类型收窄后可以使用
}
// void —— 函数无返回值
function log(msg: string): void {
console.log(msg);
}
// null 和 undefined —— 两个独立的类型
let n: null = null;
let u: undefined = undefined;
// never —— 永远不会有值的类型
function throwError(msg: string): never {
throw new Error(msg); // 函数永远不会正常返回
}
function infiniteLoop(): never {
while (true) {} // 永远不会结束
}
💡 经验法则:用 unknown 替代 any。unknown 强制你在使用前进行类型检查,更安全。
3. 字面量类型与联合类型
字面量类型
TypeScript 允许将具体的值作为类型,这在其他语言中很少见:
let yes: true = true;
// yes = false; // ❌ 类型 'false' 不能赋给类型 'true'
let httpOk: 200 = 200;
let greeting: "hello" = "hello";
联合类型(Union Types)
用 | 组合多个类型,表示"其中之一":
// 经典用法:限定有效值的范围
type Direction = "up" | "down" | "left" | "right";
function move(dir: Direction) {
console.log(`Moving ${dir}`);
}
move("up"); // ✅
// move("forward"); // ❌ 类型 '"forward"' 不能赋给类型 'Direction'
// 混合类型联合
type ID = string | number;
let userId: ID = "abc123";
userId = 42; // ✅ 也合法
可辨识联合(Discriminated Unions)
通过共有的"标签"字段区分联合中的不同类型,这是 TS 中极其常用的模式:
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rectangle":
return shape.width * shape.height; // TS 在这里知道有 width 和 height
}
}
4. 交叉类型
用 & 合并多个类型,结果类型包含所有属性:
interface HasName {
name: string;
}
interface HasAge {
age: number;
}
// 交叉类型:同时拥有 name 和 age
type Person = HasName & HasAge;
const alice: Person = {
name: "Alice",
age: 30,
};
// 实用场景:给已有类型添加字段
type Timestamped<T> = T & { createdAt: Date; updatedAt: Date };
type TimestampedPerson = Timestamped<Person>;
// 等价于 { name: string; age: number; createdAt: Date; updatedAt: Date }
💡 联合 | 是"或"——值是其中一种类型;交叉 & 是"且"——值同时满足所有类型。
5. 类型断言与非空断言
类型断言(Type Assertion)
当你比编译器更了解某个值的类型时,用 as 告诉编译器:
// DOM 操作是最常见的场景
const el = document.getElementById("app") as HTMLDivElement;
el.style.color = "red"; // 不会报错,因为你断言了它是 HTMLDivElement
// 也可以用尖括号语法(在 JSX/TSX 中不可用)
const el2 = <HTMLDivElement>document.getElementById("app");
// 双重断言 —— 在两个不相关的类型间转换(谨慎使用)
const value = "hello" as unknown as number;
非空断言(Non-null Assertion)
用 ! 后缀告诉编译器"这个值一定不是 null/undefined":
// getElementById 返回 HTMLElement | null
const el = document.getElementById("app")!; // 断言不为 null
el.style.color = "blue";
// 等价于
const el2 = document.getElementById("app");
if (el2 === null) throw new Error("Element not found");
el2.style.color = "blue";
⚠️ 类型断言和非空断言都会绕过类型检查。如果断言错误,运行时依然会崩溃。优先使用类型守卫进行安全的类型收窄。
6. 类型守卫
类型守卫让 TypeScript 在条件分支中自动收窄(narrow)类型,无需手动断言:
typeof 守卫
function padLeft(value: string | number): string {
if (typeof value === "number") {
return " ".repeat(value); // 这里 value 是 number
}
return value; // 这里 value 是 string
}
instanceof 守卫
function formatDate(input: string | Date): string {
if (input instanceof Date) {
return input.toISOString(); // input 是 Date
}
return new Date(input).toISOString(); // input 是 string
}
in 守卫
interface Fish { swim(): void; }
interface Bird { fly(): void; }
function move(animal: Fish | Bird) {
if ("swim" in animal) {
animal.swim(); // animal 是 Fish
} else {
animal.fly(); // animal 是 Bird
}
}
自定义类型守卫函数
// 返回类型 'x is Type' 是关键
function isString(value: unknown): value is string {
return typeof value === "string";
}
function process(input: unknown) {
if (isString(input)) {
console.log(input.toUpperCase()); // TS 知道 input 是 string
}
}
// 实际场景:验证 API 响应
interface ApiResponse {
data: unknown;
status: number;
}
interface User {
id: number;
name: string;
email: string;
}
function isUser(obj: unknown): obj is User {
return (
typeof obj === "object" &&
obj !== null &&
"id" in obj &&
"name" in obj &&
"email" in obj
);
}
7. TypeScript 特有运算符
这些运算符来自 JavaScript(ES2020+),但在 TypeScript 中结合类型系统尤其强大:
?? 空值合并(Nullish Coalescing)
// ?? 只在左侧为 null 或 undefined 时取右侧值
const port = config.port ?? 3000;
// 区别于 ||:|| 在左侧为任何 falsy 值时取右侧
const count1 = 0 || 10; // 10 (0 是 falsy)
const count2 = 0 ?? 10; // 0 (0 不是 null/undefined)
const name1 = "" || "默认"; // "默认"(空字符串是 falsy)
const name2 = "" ?? "默认"; // "" (空字符串不是 null/undefined)
?. 可选链(Optional Chaining)
interface Company {
name: string;
address?: {
city?: string;
street?: string;
};
}
function getCity(company: Company): string | undefined {
// 无需层层判断 null —— 任何一步为 null/undefined 就返回 undefined
return company.address?.city;
}
// 也适用于方法调用和数组索引
const result = obj.method?.(); // 方法可能不存在
const first = arr?.[0]; // 数组可能是 undefined
const value = map.get?.("key"); // get 可能不存在
=== vs ==(严格相等)
// === 严格相等:不做类型转换(推荐始终使用)
0 === "" // false
0 === false // false
null === undefined // false
// == 宽松相等:会做类型转换(避免使用)
0 == "" // true 😱
0 == false // true 😱
null == undefined // true
// TypeScript 在 strict 模式下对 == null 有特殊处理
// value == null 等价于 value === null || value === undefined
function example(value: string | null | undefined) {
if (value == null) {
// 这里 value 是 null | undefined
}
}
🔄 与 Python 的区别
Python 的 type hints 是运行时忽略的(def foo(x: int) 传入字符串不会报错),而 TypeScript 的类型在编译时强制检查。TS 的 ?. 类似 Python 中没有的语法糖;Python 需要写 getattr(obj, 'attr', None) 或链式 if 判断。
📋 本章要点
类型推断很聪明
大多数情况下不需要手写类型注解,TS 会自动推断。在函数参数和复杂场景中添加注解即可。
结构化类型系统
TS 是鸭子类型——只看结构,不看名字。与 Java/C++ 的名义类型系统截然不同。
联合类型是核心
联合类型 | 配合可辨识联合和类型守卫,是 TS 中最常用的类型建模方式。
unknown 优于 any
需要"任意类型"时用 unknown,它要求你在使用前进行类型检查,避免运行时错误。
类型守卫 > 类型断言
优先使用 typeof、instanceof、in 等类型守卫实现安全的类型收窄,而非 as 断言。
善用 ?. 和 ??
可选链 ?. 和空值合并 ?? 大幅简化 null/undefined 的处理,配合类型系统使用效果更佳。