TypeScript 入门到精通

核心理解: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\}\`: () => TK;

};

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、类型约束、可辨识参数。


十、最佳实践与精通标准

最佳实践

  1. 开启 strict。
  2. 少用 any,优先 unknown。
  3. 用类型收窄代替类型断言。
  4. 公共 API 显式导出类型。
  5. 用 satisfies 做校验同时保留推断。
  6. 用 as const 保留字面量。
  7. 用品牌类型区分同基础类型的不同 ID。
  8. 避免过度类型体操,可读性优先。
  9. 为库写类型测试。
  10. 类型驱动开发:先定义类型,再写实现。

精通标准

  • 熟练设计泛型、条件类型、映射类型、模板字面量类型。
  • 理解 infer、分布式条件类型、协变逆变。
  • 能写 .d.ts、模块增强、声明合并。
  • 能配置 tsconfig、模块解析、构建工具。
  • 能发布带类型的 npm 包。
  • 能优化大型项目类型性能。
  • 能读懂 TypeScript 编译器 API、AST。
  • 能在 React、Vue、Node、NestJS 等生态中设计类型安全 API。

学习路线建议

  1. 入门:基本类型、函数、接口、类、模块、泛型初步。
  2. 进阶:类型收窄、工具类型、条件类型、映射类型、声明文件。
  3. 高级:infer、模板字面量、类型体操、装饰器、模块增强。
  4. 工程化:tsconfig、构建、测试、Monorepo、发布。
  5. 精通:编译器 API、类型性能、类型安全架构设计。

一句话总结:

入门是会用类型,进阶是会设计类型,精通是能驾驭类型系统和工程化。

相关推荐
花旗蜕变2 小时前
前端转 NestJS 全栈实践:从表单页面到微信业务系统
前端·typescript·nestjs
艺杯羹4 小时前
Anthropic突袭发布Claude Code Mods!用TypeScript编写智能体中间件:告别AI误删与失控
人工智能·typescript·系统架构
FYKJ_20103 天前
SSM校园失物招领系统41452-计算机课程设计、毕业设计
java·vue.js·spring boot·python·mysql·typescript·spark
tiankongdeyige4 天前
游戏战斗系统学习第2篇:有限状态机 (FSM)
学习·游戏·typescript
FYKJ_20104 天前
django电影推荐系统55066-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django
进击的明明4 天前
TypeScript速通笔记(下)
前端·面试·typescript
愛芳芳4 天前
element-plus+vite+vue3+pinpa+mock+typeScript搭建经典的电商管理后台
前端·javascript·typescript
miss4 天前
从零做一个可视化规则引擎:Vue3 递归条件树 + 双引擎结果对比
前端·vue.js·typescript
seowmt5 天前
结构化数据和自然语言描述哪个更管用?答案分场景
人工智能·google·typescript·go语言·schema