04-TypeScript集成

TypeScript是JavaScript的超集,添加静态类型系统。编译期错误检查,提升代码质量和开发效率。

TypeScript基础

安装配置

npm install -D typescript @types/node @types/express
npx tsc --init

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "moduleResolution": "node",
    "declaration": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

基本类型

// 基础类型
let name: string = 'Alice';
let age: number = 25;
let isActive: boolean = true;

// 数组
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ['Alice', 'Bob'];

// 元组
let tuple: [string, number] = ['Alice', 25];

// 枚举
enum Role {
    Admin = 'ADMIN',
    User = 'USER',
    Guest = 'GUEST'
}

let role: Role = Role.Admin;

// any(避免使用)
let anything: any = 'string';
anything = 123;

// unknown(更安全)
let uncertain: unknown = 'string';
// uncertain.toUpperCase();  // 错误
if (typeof uncertain === 'string') {
    uncertain.toUpperCase();  // OK
}

// void
function log(message: string): void {
    console.log(message);
}

// never(永不返回)
function throwError(message: string): never {
    throw new Error(message);
}

// null和undefined
let nullable: string | null = null;
let optional: string | undefined = undefined;

接口和类型别名

// 接口
interface User {
    id: number;
    name: string;
    email: string;
    age?: number;  // 可选
    readonly createdAt: Date;  // 只读
}

const user: User = {
    id: 1,
    name: 'Alice',
    email: 'alice@example.com',
    createdAt: new Date()
};

// 类型别名
type ID = string | number;
type UserRole = 'admin' | 'user' | 'guest';  // 联合类型

type Point = {
    x: number;
    y: number;
};

// 交叉类型
type Admin = User & {
    permissions: string[];
};

// 泛型
interface Response<T> {
    data: T;
    status: number;
    message: string;
}

const response: Response<User> = {
    data: user,
    status: 200,
    message: 'Success'
};

Express + TypeScript

类型定义

import express, { Request, Response, NextFunction } from 'express';

const app = express();

// 基本路由
app.get('/users', (req: Request, res: Response) => {
    res.json({ users: [] });
});

// 自定义Request类型
interface AuthRequest extends Request {
    user?: {
        id: number;
        email: string;
    };
}

app.get('/profile', (req: AuthRequest, res: Response) => {
    res.json({ user: req.user });
});

// 泛型参数
app.get<{ id: string }>('/users/:id', (req, res) => {
    const { id } = req.params;  // 类型:string
    res.json({ id });
});

// 中间件类型
const authMiddleware = (
    req: AuthRequest,
    res: Response,
    next: NextFunction
): void => {
    const token = req.headers.authorization;
    if (!token) {
        res.status(401).json({ error: 'Unauthorized' });
        return;
    }
    next();
};

控制器

// controllers/userController.ts
import { Request, Response, NextFunction } from 'express';
import { User, IUser } from '../models/User';

export const getUsers = async (
    req: Request,
    res: Response,
    next: NextFunction
): Promise<void> => {
    try {
        const users = await User.find();
        res.json(users);
    } catch (err) {
        next(err);
    }
};

export const createUser = async (
    req: Request,
    res: Response,
    next: NextFunction
): Promise<void> => {
    try {
        const user: IUser = await User.create(req.body);
        res.status(201).json(user);
    } catch (err) {
        next(err);
    }
};

模型定义

// models/User.ts
import mongoose, { Document, Schema } from 'mongoose';

export interface IUser extends Document {
    name: string;
    email: string;
    age: number;
    createdAt: Date;
}

const userSchema = new Schema<IUser>({
    name: { type: String, required: true },
    email: { type: String, required: true, unique: true },
    age: { type: Number, default: 0 },
    createdAt: { type: Date, default: Date.now }
});

export const User = mongoose.model<IUser>('User', userSchema);

类型守卫

类型断言

// as断言
const value: unknown = 'hello';
const strLength = (value as string).length;

// 非空断言
function getValue(id?: number): number {
    return id!;  // 断言id非undefined
}

// 类型守卫函数
function isString(value: unknown): value is string {
    return typeof value === 'string';
}

if (isString(value)) {
    console.log(value.toUpperCase());  // value确定是string
}

// 自定义类型守卫
interface User {
    type: 'user';
    name: string;
}

interface Admin {
    type: 'admin';
    name: string;
    permissions: string[];
}

type Person = User | Admin;

function isAdmin(person: Person): person is Admin {
    return person.type === 'admin';
}

function printPerson(person: Person) {
    if (isAdmin(person)) {
        console.log(person.permissions);  // person是Admin
    } else {
        console.log(person.name);  // person是User
    }
}

装饰器

启用装饰器

// tsconfig.json
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

常用装饰器

// 类装饰器
function Controller(path: string) {
    return function<T extends { new(...args: any[]): {} }>(constructor: T) {
        return class extends constructor {
            basePath = path;
        };
    };
}

@Controller('/api/users')
class UserController {
    // ...
}

// 方法装饰器
function Log(target: any, key: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    
    descriptor.value = function(...args: any[]) {
        console.log(`调用 ${key},参数:`, args);
        const result = originalMethod.apply(this, args);
        console.log(`${key} 返回:`, result);
        return result;
    };
    
    return descriptor;
}

class Calculator {
    @Log
    add(a: number, b: number): number {
        return a + b;
    }
}

// 参数装饰器
function Required(target: any, key: string, index: number) {
    // 标记必需参数
}

class Service {
    method(@Required param: string) {
        // ...
    }
}

常用工具类型

// Partial:所有属性可选
interface User {
    id: number;
    name: string;
    email: string;
}

type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; }

// Required:所有属性必需
type RequiredUser = Required<PartialUser>;

// Readonly:所有属性只读
type ReadonlyUser = Readonly<User>;

// Pick:选择部分属性
type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string; }

// Omit:排除部分属性
type UserWithoutId = Omit<User, 'id'>;
// { name: string; email: string; }

// Record:键值对类型
type UserMap = Record<number, User>;
// { [key: number]: User }

// Exclude:排除类型
type T1 = Exclude<'a' | 'b' | 'c', 'a'>;  // 'b' | 'c'

// Extract:提取类型
type T2 = Extract<'a' | 'b' | 'c', 'a' | 'b'>;  // 'a' | 'b'

// NonNullable:排除null和undefined
type T3 = NonNullable<string | null | undefined>;  // string

// ReturnType:获取函数返回类型
function getUser() {
    return { id: 1, name: 'Alice' };
}

type User = ReturnType<typeof getUser>;
// { id: number; name: string; }

配置开发环境

ts-node开发

npm install -D ts-node nodemon

# nodemon.json
{
  "watch": ["src"],
  "ext": "ts",
  "exec": "ts-node src/index.ts"
}

# package.json
{
  "scripts": {
    "dev": "nodemon",
    "build": "tsc",
    "start": "node dist/index.js"
  }
}

ESLint + TypeScript

npm install -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
// .eslintrc.json
{
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"],
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "rules": {
    "@typescript-eslint/no-explicit-any": "error",
    "@typescript-eslint/explicit-function-return-type": "warn"
  }
}

最佳实践

类型定义

// ✓ 明确类型
function add(a: number, b: number): number {
    return a + b;
}

// ❌ 避免any
function process(data: any) {  // 丧失类型检查
    return data.value;
}

// ✓ 使用泛型
function process<T>(data: T): T {
    return data;
}

// ✓ 使用unknown替代any
function process(data: unknown) {
    if (typeof data === 'object' && data !== null) {
        // 类型收窄
    }
}

类型收窄

function printValue(value: string | number) {
    if (typeof value === 'string') {
        console.log(value.toUpperCase());  // value是string
    } else {
        console.log(value.toFixed(2));  // value是number
    }
}

// in操作符
interface Cat {
    meow: () => void;
}

interface Dog {
    bark: () => void;
}

function makeSound(animal: Cat | Dog) {
    if ('meow' in animal) {
        animal.meow();  // animal是Cat
    } else {
        animal.bark();  // animal是Dog
    }
}

严格模式

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,  // 启用所有严格检查
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "alwaysStrict": true
  }
}

核心: TypeScript通过静态类型系统在编译期发现错误,提升代码质量和可维护性。合理使用类型定义和泛型是关键。