核心理解:TypeScript = JavaScript + 静态类型系统,编译后类型会被擦除,运行时仍是 JS。
一、入门基础
1. 安装与编译
npm i -D typescript
npx tsc --init
npx tsc index.ts
tsconfig.json 基础:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"strict": true ,
"outDir": "dist",
"rootDir": "src"
},
"include": "src"
}
2. 基本类型
let isDone: boolean = false ;
let count: number = 10;
let name: string = "Alice";
let big: bigint = 100n;
let sym: symbol = Symbol("id");
let n: null = null ;
let u: undefined = undefined ;
function log(msg: string): void {
console.log(msg);
}
let anything: any = 1;
anything.foo(); // 不报错,但不安全
let safe: unknown = 1;
// safe.toFixed(); // 报错,必须先收窄
if (typeof safe === "number") {
safe.toFixed();
}
function fail(msg: string): never {
throw new Error(msg);
}
3. 类型推断
let age = 18; // number
let username = "Tom"; // string
const PI = 3.14; // 3.14 字面量类型
4. 数组、元组、枚举
let list: number\[\] = 1, 2, 3;
let list2: Array<string> = "a", "b";
let tuple: string, number = "age", 18;
tuple0.toUpperCase();
enum Direction {
Up = 1,
Down,
Left,
Right,
}
enum Status {
Success = "SUCCESS",
Error = "ERROR",
}
5. 联合类型、交叉类型、字面量类型
type ID = string | number;
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged;
type Dir = "up" | "down" | "left" | "right";
let d: Dir = "up";
// d = "xxx"; // 报错
6. type 与 interface
interface User {
id: number;
name: string;
age?: number;
readonly createdAt: Date;
}
interface User {
email: string; // 声明合并
}
type Point = {
x: number;
y: number;
};
// type Point = { z: number }; // 报错,type 不能重复声明
区别:
- interface 可声明合并,适合对象、类实现。
- type 更灵活,可表示联合、交叉、条件类型、元组等。
二、函数、对象与类
1. 函数类型
function add(a: number, b: number): number {
return a + b;
}
function greet(name: string, prefix = "Hello", ...rest: string\[\]): string {
return `prefix,{name} ${rest.join(" ")}`;
}
const fn: (x: number, y: number) => number = (x, y) => x + y;
function parse(input: string): string\[\];
function parse(input: number): number\[\];
function parse(input: string | number): string\[\] | number\[\] {
return typeof input === "string" ? input.split("") : input;
}
2. 对象类型
interface Config {
readonly host: string;
port?: number;
key: string: unknown;
}
const config: Config = {
host: "localhost",
port: 3000,
debug: true ,
};
3. 类
abstract class Animal {
constructor (public name: string) {}
abstract speak(): string;
move(): void {
console.log(`${this .name} is moving`);
}
}
interface Pet {
owner: string;
}
class Dog extends Animal implements Pet {
constructor (name: string, public owner: string) {
super (name);
}
speak(): string {
return "Woof";
}
}
const dog = new Dog("旺财", "Tom");
dog.move();
console.log(dog.speak());
修饰符:
class User {
public name: string;
private password: string;
protected age: number;
readonly id: number;
static count = 0;
constructor (name: string, password: string, age: number, id: number) {
this .name = name;
this .password = password;
this .age = age;
this .id = id;
User.count++;
}
}
参数属性简写:
class User2 {
constructor (
public name: string,
private password: string,
readonly id: number
) {}
}
三、泛型
1. 泛型函数
function identity<T>(arg: T): T {
return arg;
}
const a = identity<string>("hello");
const b = identity(123); // 自动推断 T = number
2. 泛型约束
function getLength<T extends { length: number }>(arg: T): number {
return arg.length;
}
getLength("abc");
getLength(1, 2, 3);
// getLength(123); // 报错
3. 泛型接口与类
interface Box<T> {
value: T;
}
class Stack<T> {
private items: T\[\] = \[\];
push(item: T): void {
this .items.push(item);
}
pop(): T | undefined {
return this .items.pop();
}
}
4. 默认泛型
interface ApiResponse<T = unknown> {
code: number;
data: T;
message: string;
}
const res: ApiResponse = {
code: 0,
data: null,
message: "ok",
};
5. keyof、typeof、索引访问
type User = {
id: number;
name: string;
};
type UserKeys = keyof User; // "id" | "name"
type UserName = User"name"; // string
const user = { id: 1, name: "Tom" };
type UserType = typeof user;
function pluck<T, K extends keyof T>(obj: T, key: K): TK {
return objkey;
}
const name = pluck(user, "name"); // string
四、类型收窄与类型守卫
1. typeof、instanceof、in
function padLeft(padding: number | string, input: string): string {
if (typeof padding === "number") {
return " ".repeat(padding) + input;
}
return padding + input;
}
class Cat {
meow() {}
}
class Dog {
bark() {}
}
function speak(animal: Cat | Dog) {
if (animal instanceof Cat) {
animal.meow();
} else {
animal.bark();
}
}
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ("swim" in animal) {
animal.swim();
} else {
animal.fly();
}
}
2. 可辨识联合
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.side ** 2;
}
}
3. 类型谓词与断言函数
function isString(value: unknown): value is string {
return typeof value === "string";
}
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== "string") {
throw new Error("Not a string");
}
}
function assert(condition: unknown, msg?: string): asserts condition {
if (!condition) throw new Error(msg);
}
五、高级类型
1. 映射类型
type MyPartial<T> = {
K **in** **keyof** T?: TK;
};
type MyReadonly<T> = {
readonly K **in** **keyof** T: TK;
};
type MyPick<T, K extends keyof T> = {
P **in** K: TP;
};
interface Todo {
id: number;
title: string;
done: boolean;
}
type PartialTodo = MyPartial<Todo>;
type ReadonlyTodo = MyReadonly<Todo>;
type TodoTitle = MyPick<Todo, "id" | "title">;
2. 条件类型
type IsString<T> = T extends string ? true : false ;
type A = IsString<string>; // true
type B = IsString<number>; // false
分布式条件类型:
type ToArray<T> = T extends any ? T\[\] : never;
type R = ToArray<string | number>; // string\[\] | number\[\]
3. infer
type ElementType<T> = T extends (infer U)\[\] ? U : never;
type E1 = ElementType<string\[\]>; // string
type E2 = ElementType<number\[\]>; // number
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type P = UnwrapPromise<Promise<string>>; // string
type MyReturnType<T> = T extends (...args: any\[\]) => infer R ? R : never;
type FnReturn = MyReturnType<() => number>; // number
4. 模板字面量类型
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">; // "onClick"
type FocusEvent = EventName<"focus">; // "onFocus"
type CSSValue = `numberpx`|`{number}rem` | `${number}%`;
const width: CSSValue = "10px";
键重映射:
type Getters<T> = {
K **in** **keyof** T **as** \`get${Capitalize\
};
interface Person {
name: string;
age: number;
}
type PersonGetters = Getters<Person>;
// {
// getName: () => string;
// getAge: () => number;
// }
5. 内置工具类型
interface User {
id: number;
name: string;
age: number;
}
type PartialUser = Partial<User>;
type RequiredUser = Required<User>;
type ReadonlyUser = Readonly<User>;
type PickUser = Pick<User, "id" | "name">;
type OmitUser = Omit<User, "age">;
type RecordUser = Record<string, User>;
type ExcludeType = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type ExtractType = Extract<"a" | "b" | "c", "a" | "c">; // "a" | "c"
type NonNull = NonNullable<string | null | undefined>; // string
type Return = ReturnType<() => string>; // string
type Params = Parameters<(a: number, b: string) => void>; // a: number, b: string
type AwaitedType = Awaited<Promise<Promise<number>>>; // number
6. as const、satisfies
const arr = 1, 2 as const ;
// readonly 1, 2
const obj = { name: "Tom" } as const ;
// { readonly name: "Tom" }
const palette = {
red: 255, 0, 0,
green: "#00ff00",
} satisfies Record<string, string | number\[\]>;
palette.green.toUpperCase(); // OK,green 被推断为 string
palette.red.map((v) => v.toFixed()); // OK
const config = {
port: 3000,
host: "localhost",
} as const satisfies { port: number; host: string };
// config.port 类型是 3000
7. 品牌类型
type Brand<T, B> = T & { readonly __brand: B };
type UserId = Brand<string, "UserId">;
type OrderId = Brand<string, "OrderId">;
function createUserId(id: string): UserId {
return id as UserId;
}
function getOrder(id: OrderId) {}
// getOrder(createUserId("u1")); // 报错,类型不匹配
8. 递归类型
type Json =
| string
| number
| boolean
| null
| Json\[\]
| { key: string: Json };
const data: Json = {
name: "Tom",
age: 18,
tags: "a", "b",
address: {
city: "杭州",
},
};
9. const 类型参数
function tuple<const T extends readonly unknown\[\]>(...args: T): T {
return args;
}
const t = tuple(1, "a", true );
// readonly 1, "a", true
六、模块、声明文件与命名空间
1. 模块
// types.ts
export interface User {
id: number;
name: string;
}
export type UserId = User"id";
// main.ts
import type { User, UserId } from "./types";
export function getUser(): User {
return { id: 1, name: "Tom" };
}
推荐使用 import type / export type 只导入类型。
2. 声明文件 .d.ts
// global.d.ts
declare global {
interface Window {
APP_VERSION: string;
}
}
export {};
// modules.d.ts
declare module "*.css" {
const content: string;
export default content;
}
3. 模块增强
// express.d.ts
declare module "express" {
interface Request {
user?: {
id: string;
name: string;
};
}
}
export {};
4. 命名空间
namespace Utils {
export function log(msg: string) {
console.log(msg);
}
}
Utils.log("hello");
现代项目更推荐 ES Module,命名空间主要用于声明文件或老代码。
七、装饰器
传统实验装饰器:
function Log(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value;
descriptor.value = function (...args: any\[\]) {
console.log(`Calling ${propertyKey} with`, args);
return original.apply(this , args);
};
}
class Calculator {
@Log
add(a: number, b: number): number {
return a + b;
}
}
const calc = new Calculator();
calc.add(1, 2);
需要:
{
"compilerOptions": {
"experimentalDecorators": true ,
"emitDecoratorMetadata": true
}
}
装饰器可用于类、方法、属性、参数、访问器,常用于 NestJS、Angular、TypeORM 等。
八、工程化与工具链
1. tsconfig 关键选项
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true ,
"noImplicitAny": true ,
"strictNullChecks": true ,
"esModuleInterop": true ,
"skipLibCheck": true ,
"forceConsistentCasingInFileNames": true ,
"declaration": true ,
"declarationMap": true ,
"sourceMap": true ,
"outDir": "dist",
"rootDir": "src",
"isolatedModules": true ,
"verbatimModuleSyntax": true
},
"include": "src"
}
重点:
- strict:开启严格模式全家桶。
- noImplicitAny:禁止隐式 any。
- strictNullChecks:null / undefined 必须显式处理。
- moduleResolution:模块解析策略。
- declaration:生成 .d.ts。
- isolatedModules:确保每个文件可独立编译。
- verbatimModuleSyntax:更严格地区分类型导入和值导入。
2. 构建工具
- tsc:官方编译器。
- ts-node / tsx:直接运行 TS。
- esbuild / swc:极速编译。
- vite / webpack / rollup:前端构建。
- tsup / unbuild:库打包。
3. 代码规范与测试
npm i -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
npm i -D prettier
npm i -D vitest
npm i -D tsd expect-type
类型测试示例:
import { expectTypeOf } from "expect-type";
expectTypeOf<string>().toEqualTypeOf<string>();
expectTypeOf<Promise<number>>().toEqualTypeOf<Promise<number>>();
4. 发布带类型的 npm 包
{
"name": "my-lib",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
}
}
九、精通级综合示例:类型安全事件总线
type EventMap = {
login: { userId: string; name: string };
logout: void;
message: { from: string; content: string };
};
class EventBus<Events extends Record<string, unknown>> {
private handlers: {
K **in** **keyof** Events?: Array<(payload: EventsK) => void>;
} = {};
on<K extends keyof Events>(
event: K,
handler: (payload: EventsK) => void
): void {
const list = this .handlersevent ?? \[\];
list.push(handler);
this .handlersevent = list;
}
emit<K extends keyof Events>(event: K, payload: EventsK): void {
this .handlersevent?.forEach((handler) => handler(payload));
}
}
const bus = new EventBus<EventMap>();
bus.on("login", (payload) => {
console.log(payload.userId, payload.name);
});
bus.on("message", (payload) => {
console.log(payload.from, payload.content);
});
bus.emit("login", { userId: "1", name: "Tom" });
bus.emit("logout", undefined );
// bus.emit("login", { userId: 1 }); // 报错
这个例子用到了:泛型、映射类型、索引访问、keyof、Record、类型约束、可辨识参数。
十、最佳实践与精通标准
最佳实践
- 开启 strict。
- 少用 any,优先 unknown。
- 用类型收窄代替类型断言。
- 公共 API 显式导出类型。
- 用 satisfies 做校验同时保留推断。
- 用 as const 保留字面量。
- 用品牌类型区分同基础类型的不同 ID。
- 避免过度类型体操,可读性优先。
- 为库写类型测试。
- 类型驱动开发:先定义类型,再写实现。
精通标准
- 熟练设计泛型、条件类型、映射类型、模板字面量类型。
- 理解 infer、分布式条件类型、协变逆变。
- 能写 .d.ts、模块增强、声明合并。
- 能配置 tsconfig、模块解析、构建工具。
- 能发布带类型的 npm 包。
- 能优化大型项目类型性能。
- 能读懂 TypeScript 编译器 API、AST。
- 能在 React、Vue、Node、NestJS 等生态中设计类型安全 API。
学习路线建议
- 入门:基本类型、函数、接口、类、模块、泛型初步。
- 进阶:类型收窄、工具类型、条件类型、映射类型、声明文件。
- 高级:infer、模板字面量、类型体操、装饰器、模块增强。
- 工程化:tsconfig、构建、测试、Monorepo、发布。
- 精通:编译器 API、类型性能、类型安全架构设计。
一句话总结:
入门是会用类型,进阶是会设计类型,精通是能驾驭类型系统和工程化。