𝑻𝒆𝒏𝑪𝒍𝒂𝒘正在头脑风暴···
𝑻𝒆𝒏𝑲𝒊𝑺𝒆𝒀𝒂の𝑨𝒈𝒆𝒏𝒕助手
𝑻𝒆𝒏-𝒇𝒍𝒂𝒔𝒉

TypeScript入门到精通指南

还记得我刚接触TypeScript的时候,心里是有点抗拒的。JavaScript用得好好的,为什么要额外学一门新的语言?但随着项目越来越复杂,我渐渐体会到TypeScript带来的好处。今天,我就以过来人的经验,带你走进TypeScript的世界,让你少走弯路。

一、为什么要学习TypeScript?

动态类型 vs 静态类型

JavaScript是一门动态类型语言,这意味着:

// JavaScript中,变量类型可以随时改变
let message = "Hello" // string
message = 42 // number
message = true // boolean
message = { name: "张三" } // object

// 函数参数类型不确定
function add(a, b) {
return a + b
}

add(1, 2) // 3
add("1", "2") // "12"
add(1, "2") // "1" - 这可能不是我们想要的结果

而TypeScript引入了静态类型检查:

// TypeScript中,类型一旦确定就不能改变
let message: string = "Hello"
// message = 42 // 编译时会报错

// 函数参数和返回值类型明确
function add(a: number, b: number): number {
return a + b
}

add(1, 2) // ✅ 3
add("1", "2") // ❌ 编译时报错

TypeScript的优势

  1. 类型安全:在编译阶段就能发现类型错误,避免运行时bug
  2. 更好的IDE支持:自动补全、类型提示、错误检查
  3. 代码可维护性:清晰的类型定义让代码更易理解和维护
  4. 重构信心:修改代码时,TypeScript能帮你找到所有相关的地方
  5. 大型项目必备:在大型项目中,类型定义是团队协作的基础

二、基础类型

原始类型

// boolean
let isActive: boolean = true

// number - 支持十进制、十六进制、二进制、八进制
let decimal: number = 42
let hex: number = 0xff
let binary: number = 0b1010
let octal: number = 0o744

// string - 支持模板字符串
let name: string = "张三"
let greeting: string = `Hello, ${name}!`

// null 和 undefined
let nullValue: null = null
let undefinedValue: undefined = undefined

// void - 表示没有返回值
function logMessage(message: string): void {
console.log(message)
}

// never - 表示永不返回的函数
function throwError(message: string): never {
throw new Error(message)
}

// any - 任意类型(不推荐使用)
let anything: any = "可以是任何类型"
anything = 42
anything = true

// unknown - 安全的any类型
let unknownValue: unknown
unknownValue = "hello"
unknownValue = 42

// 使用unknown需要类型检查
if (typeof unknownValue === "string") {
console.log(unknownValue.toUpperCase())
}

数组类型

// 方式1:元素类型[]
let numbers: number[] = [1, 2, 3, 4, 5]
let names: string[] = ["张三", "李四", "王五"]

// 方式2:Array<元素类型>
let numbers2: Array<number> = [1, 2, 3, 4, 5]

// 只读数组
const readonlyNumbers: readonly number[] = [1, 2, 3]
// readonlyNumbers[0] = 4 // ❌ 错误

// 元组 - 固定长度的数组
let person: [string, number] = ["张三", 25]

// 可以使用解构
const [name, age] = person

// 可选元组元素
let optionalTuple: [string, number?] = ["张三"]

// 可变元组
let restTuple: [string, ...number[]] = ["张三", 1, 2, 3, 4]

对象类型

// 基本对象类型
let user: {
name: string
age: number
email?: string // 可选属性
} = {
name: "张三",
age: 25
}

// 接口定义
interface User {
name: string
age: number
email?: string
readonly id: number // 只读属性
}

const user2: User = {
name: "李四",
age: 30,
id: 123
}

// user2.id = 456 // ❌ 错误,id是只读的

// 索引签名
interface StringDictionary {
[key: string]: string
}

const dict: StringDictionary = {
name: "张三",
age: "25"
}

// 多个索引签名
interface MixedDictionary {
[key: string]: string | number
[key: number]: string
}

// 函数类型
interface Callback {
(error: Error | null, data: string): void
}

const callback: Callback = (err, data) => {
if (err) {
console.error(err)
} else {
console.log(data)
}
}

三、高级类型

联合类型

// 基本联合类型
let id: string | number
id = "abc"
id = 123

// 字面量联合类型
type Direction = "up" | "down" | "left" | "right"

function move(direction: Direction): void {
console.log(`Moving ${direction}`)
}

move("up") // ✅
move("upward" // ❌

交叉类型

type Person = {
name: string
age: number
}

type Employee = {
id: number
position: string
}

type EmployeeInfo = Person & Employee

const emp: EmployeeInfo = {
name: "张三",
age: 25,
id: 123,
position: "前端开发"
}

// 使用交叉类型合并多个接口
interface CanSwim {
swim(): void
}

interface CanFly {
fly(): void
}

class Duck implements CanSwim, CanFly {
swim() {
console.log("Swimming")
}

fly() {
console.log("Flying")
}
}

类型别名

// 基本类型别名
type ID = string | number
type UserID = string
type ProductID = number

// 复杂类型别名
type Point = {
x: number
y: number
}

// 创建类型别名
type Coordinates = Point & {
z?: number
}

// 泛型类型别名
type Container<T> = { value: T }
const numberContainer: Container<number> = { value: 123 }
const stringContainer: Container<string> = { value: "hello" }

// 条件类型
type ExtractType<T> = T extends string ? string : number

type StrType = ExtractType<string> // string
type NumType = ExtractType<number> // number
type BoolType = ExtractType<boolean> // number

类型推断

// 自动推断
let count = 0 // number
const message = "Hello" // string

// 函数参数和返回值推断
function add(a: number, b: number) {
return a + b
}

// let result = add(1, 2) // number

// 对象字面量推断
const person = {
name: "张三",
age: 25
}

// person.age = "twenty-five" // ❌ 错误

// 类型断言
let someValue: any = "this is a string"
let strLength: number = (someValue as string).length

// 非空断言
function greet(name?: string) {
// name! 表示name不为undefined
console.log(`Hello, ${name}!`)
}

greet() // "Hello, undefined!"

四、接口

基础接口

// 简单接口
interface User {
name: string
age: number
}

// 可选属性
interface UserProfile {
name: string
age: number
email?: string
phone?: string
}

// 只读属性
interface ReadOnlyUser {
readonly id: number
name: string
age: number
}

const readonlyUser: ReadOnlyUser = {
id: 123,
name: "张三",
age: 25
}

// readonlyUser.id = 456 // ❌ 错误

// 函数接口
interface AddFunction {
(a: number, b: number): number
}

const add: AddFunction = (x, y) => x + y

// 可索引接口
interface StringArray {
[index: number]: string
}

const myArray: StringArray = ["张三", "李四", "王五"]
console.log(myArray[0]) // "张三"

// 接口继承
interface Animal {
name: string
age: number
}

interface Dog extends Animal {
breed: string
}

const dog: Dog = {
name: "旺财",
age: 3,
breed: "金毛"
}

接口的扩展

// 多重继承
interface Shape {
color: string
}

interface Circle {
radius: number
}

interface RedCircle extends Shape, Circle {
isRed: boolean
}

const redCircle: RedCircle = {
color: "red",
radius: 10,
isRed: true
}

// 接口实现
interface Clock {
currentTime: Date
setTime(d: Date): void
}

class DigitalClock implements Clock {
currentTime: Date = new Date()

setTime(d: Date) {
this.currentTime = d
}
}

// 混合类型接口
interface Counter {
(): number
count: number
increment(): void
decrement(): void
}

function getCounter(): Counter {
let count = 0
const counter = () => count
counter.count = count
counter.increment = () => { count++ }
counter.decrement = () => { count-- }
return counter
}

const myCounter = getCounter()
console.log(myCounter()) // 0
myCounter.increment()
console.log(myCounter()) // 1
console.log(myCounter.count) // 1

五、类

基础类

class Animal {
name: string
age: number

constructor(name: string, age: number) {
this.name = name
this.age = age
}

eat() {
console.log(`${this.name} is eating`)
}

sleep() {
console.log(`${this.name} is sleeping`)
}
}

class Dog extends Animal {
breed: string

constructor(name: string, age: number, breed: string) {
super(name, age)
this.breed = breed
}

bark() {
console.log(`${this.name} is barking`)
}

// 重写方法
eat() {
console.log(`${this.name} the ${this.breed} is eating`)
}
}

const dog = new Dog("旺财", 3, "金毛")
dog.eat() // "旺财 the 金毛 is eating"
dog.bark() // "旺财 is barking"

修饰符

class Person {
public name: string // 公开,可以在任何地方访问
private age: number // 私有,只能在类内部访问
protected gender: string // 保护,可以在类和子类中访问
readonly id: number // 只读,一旦赋值不能修改

constructor(name: string, age: number, gender: string, id: number) {
this.name = name
this.age = age
this.gender = gender
this.id = id
}

public getAge(): number {
return this.age // ✅ 可以访问私有属性
}

protected getGender(): string {
return this.gender
}
}

class Employee extends Person {
constructor(name: string, age: number, gender: string, id: number, public position: string) {
super(name, age, gender, id)
}

getInfo(): string {
// this.age // ❌ 不能访问私有属性
return `Name: ${this.name}, Gender: ${this.getGender()}, Position: ${this.position}`
}
}

const emp = new Employee("张三", 25, "男", 123, "前端开发")
console.log(emp.name) // ✅ "张三"
console.log(emp.getInfo()) // ✅ "Name: 张三, Gender: 男, Position: 前端开发"
// console.log(emp.age) // ❌ 错误

存取器

class Temperature {
private _celsius: number

constructor(celsius: number) {
this._celsius = celsius
}

// getter
get celsius(): number {
return this._celsius
}

// setter
set celsius(value: number) {
if (value < -273.15) {
throw new Error("Temperature cannot be below absolute zero")
}
this._celsius = value
}

get fahrenheit(): number {
return this._celsius * 9/5 + 32
}

set fahrenheit(value: number) {
this.celsius = (value - 32) * 5/9
}
}

const temp = new Temperature(25)
console.log(temp.celsius) // 25
console.log(temp.fahrenheit) // 77

temp.celsius = 30
console.log(temp.fahrenheit) // 86

静态成员

class MathUtils {
static PI: number = 3.14159

static circleArea(radius: number): number {
return MathUtils.PI * radius * radius
}

static squareArea(side: number): number {
return side * side
}
}

console.log(MathUtils.PI) // 3.14159
console.log(MathUtils.circleArea(5)) // 78.53975
console.log(MathUtils.squareArea(4)) // 16

// 静态方法可以通过this调用
class Calculator {
static add(a: number, b: number): number {
return a + b
}

static multiply(a: number, b: number): number {
return a * b
}

static calculate(operation: 'add' | 'multiply', a: number, b: number): number {
switch (operation) {
case 'add':
return this.add(a, b)
case 'multiply':
return this.multiply(a, b)
default:
throw new Error('Invalid operation')
}
}
}

六、泛型

基础泛型

// 简单泛型函数
function identity<T>(arg: T): T {
return arg
}

// 使用
let output1 = identity<string>("hello")
let output2 = identity(42) // 类型推断为number

// 泛型接口
interface GenericBox<T> {
value: T
}

const numberBox: GenericBox<number> = { value: 123 }
const stringBox: GenericBox<string> = { value: "hello" }

// 泛型类
class GenericArray<T> {
private items: T[] = []

add(item: T): void {
this.items.push(item)
}

remove(item: T): void {
const index = this.items.indexOf(item)
if (index > -1) {
this.items.splice(index, 1)
}
}

getAll(): T[] {
return [...this.items]
}
}

const numberArray = new GenericArray<number>()
numberArray.add(1)
numberArray.add(2)
numberArray.add(3)
console.log(numberArray.getAll()) // [1, 2, 3]

泛型约束

// 基本约束
interface Lengthwise {
length: number
}

function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length)
return arg
}

logLength("hello") // ✅ string有length
logLength([1, 2, 3]) // ✅ array有length
logLength({ length: 10 }) // ✅ 对象有length
// logLength(42) // ❌ number没有length

// 多个约束
interface HasName {
name: string
}

interface HasAge {
age: number
}

function getDisplayName<T extends HasName & HasAge>(person: T): string {
return `${person.name} (${person.age}岁)`
}

const person = { name: "张三", age: 25 }
console.log(getDisplayName(person)) // "张三 (25岁)"

泛型工具类型

// Partial - 将所有属性变为可选
interface User {
name: string
age: number
email: string
}

type PartialUser = Partial<User>
// 等价于:
// {
// name?: string
// age?: number
// email?: string
// }

const partialUser: PartialUser = { name: "张三" }

// Required - 将所有属性变为必需
type RequiredUser = Required<User>
// 等价于:
// {
// name: string
// age: number
// email: string
// }

// Pick - 选择特定属性
type UserName = Pick<User, 'name'>
// 等价于:{ name: string }

type UserAgeEmail = Pick<User, 'age' | 'email'>
// 等价于:{ age: number, email: string }

// Omit - 排除特定属性
type UserWithoutEmail = Omit<User, 'email'>
// 等价于:{ name: string, age: number }

// Record - 创建对象类型
type UserRoles = Record<string, string>
const roles: UserRoles = {
admin: "管理员",
editor: "编辑",
viewer: "查看者"
}

// Exclude - 排除类型
type Primitive = string | number | boolean
type NonString = Exclude<Primitive, string> // number | boolean

// Extract - 提取类型
type StringOrNumber = Extract<Primitive, string | number> // string | number

// ReturnType - 获取函数返回类型
function getUser(): { name: string; age: number } {
return { name: "张三", age: 25 }
}

type UserReturnType = ReturnType<typeof getUser> // { name: string; age: number }

// Parameters - 获取函数参数类型
function createUser(name: string, age: number): void {}
type CreateUserParams = Parameters<typeof createUser> // [string, number]

七、装饰器

装饰器基础

// 类装饰器
function classDecorator<T extends new (...args: any[]) => any>(constructor: T) {
return class extends constructor {
newProperty = "new property"

greeting() {
console.log("Hello from decorated class")
}
}
}

@classDecorator
class Person {
name: string

constructor(name: string) {
this.name = name
}
}

const person = new Person("张三")
// console.log(person.newProperty) // "new property"
// person.greeting() // "Hello from decorated class"

// 方法装饰器
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value

descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey} with args:`, args)
const result = originalMethod.apply(this, args)
console.log(`${propertyKey} returned:`, result)
return result
}
}

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

const calc = new Calculator()
calc.add(2, 3) // 会打印日志

// 属性装饰器
function format(target: any, propertyKey: string) {
let value = target[propertyKey]

const getter = function() {
return value
}

const setter = function(newVal: string) {
value = newVal.toUpperCase()
}

Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
})
}

class TextProcessor {
@format
text: string

constructor(text: string) {
this.text = text
}
}

const processor = new TextProcessor("hello")
processor.text = "world" // 自动转为大写
console.log(processor.text) // "WORLD"

// 参数装饰器
function logParameter(target: any, propertyKey: string, parameterIndex: number) {
console.log(`Parameter at index ${parameterIndex} in method ${propertyKey} was called`)
}

class UserService {
createUser(@logParameter name: string, @logParameter age: number) {
console.log(`Creating user: ${name}, ${age}`)
}
}

const userService = new UserService()
userService.createUser("张三", 25)

装饰器工厂

// 装饰器工厂 - 可以接受参数的装饰器
function configurable(value: boolean) {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.configurable = value
}
}

class Greeter {
greeting: string

constructor(message: string) {
this.greeting = message
}

@configurable(false)
greet() {
return "Hello, " + this.greeting
}
}

// 多个装饰器组合
function first() {
console.log("first(): factory evaluated")
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("first(): called")
}
}

function second() {
console.log("second(): factory evaluated")
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("second(): called")
}
}

class Example {
@first()
@second()
method() {}
}

// 执行顺序:
// first(): factory evaluated
// second(): factory evaluated
// second(): called
// first(): called

八、模块

基本模块

// math.ts - 模块文件
export const PI = 3.14159

export function add(a: number, b: number): number {
return a + b
}

export function multiply(a: number, b: number): number {
return a * b
}

export class Calculator {
constructor(public name: string) {}

calculate(operation: 'add' | 'multiply', a: number, b: number): number {
switch (operation) {
case 'add':
return add(a, b)
case 'multiply':
return multiply(a, b)
default:
throw new Error('Invalid operation')
}
}
}

// index.ts - 主模块文件
export * from './math' // 导出math.ts中的所有内容
export { add as sum } from './math' // 重新导出并重命名

// app.ts - 使用模块
import { PI, add, multiply, Calculator } from './math'

console.log(PI) // 3.14159
console.log(add(2, 3)) // 5
console.log(multiply(2, 3)) // 6

const calc = new Calculator("My Calculator")
console.log(calc.calculate('add', 5, 7)) // 12
console.log(calc.calculate('multiply', 5, 7)) // 35

模块命名空间

// validation.ts
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean
}

export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string): boolean {
return s.length === 5 && /^[0-9]+$/.test(s)
}
}

export class EmailValidator implements StringValidator {
isAcceptable(s: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s)
}
}
}

// app.ts
const validators: Validation.StringValidator[] = [
new Validation.ZipCodeValidator(),
new Validation.EmailValidator()
]

console.log(validators[0].isAcceptable("90210")) // true
console.log(validators[1].isAcceptable("test@example.com")) // true

动态导入

// 动态导入模块
async function loadModule() {
const mathModule = await import('./math')
console.log(mathModule.PI) // 3.14159
console.log(mathModule.add(2, 3)) // 5
}

loadModule()

// 条件导入
if (process.env.NODE_ENV === 'production') {
import('./productionUtils').then(prodUtils => {
prodUtils.optimizeProductionCode()
})
}

// 解构动态导入
async function createValidator() {
const { ZipCodeValidator, EmailValidator } = await import('./validation')
const zipValidator = new ZipCodeValidator()
const emailValidator = new EmailValidator()

return { zipValidator, emailValidator }
}

九、实战案例

案例1:用户管理系统

// types.ts
interface User {
id: number
name: string
email: string
age: number
createdAt: Date
updatedAt: Date
}

interface CreateUserInput {
name: string
email: string
age: number
}

interface UpdateUserInput {
name?: string
email?: string
age?: number
}

// repositories/userRepository.ts
interface UserRepository {
findById(id: number): Promise<User | null>
findAll(): Promise<User[]>
create(data: CreateUserInput): Promise<User>
update(id: number, data: UpdateUserInput): Promise<User>
delete(id: number): Promise<boolean>
}

class InMemoryUserRepository implements UserRepository {
private users: User[] = []
private nextId = 1

async findById(id: number): Promise<User | null> {
return this.users.find(user => user.id === id) || null
}

async findAll(): Promise<User[]> {
return [...this.users]
}

async create(data: CreateUserInput): Promise<User> {
const user: User = {
id: this.nextId++,
...data,
createdAt: new Date(),
updatedAt: new Date()
}
this.users.push(user)
return user
}

async update(id: number, data: UpdateUserInput): Promise<User> {
const userIndex = this.users.findIndex(user => user.id === id)
if (userIndex === -1) {
throw new Error('User not found')
}

this.users[userIndex] = {
...this.users[userIndex],
...data,
updatedAt: new Date()
}

return this.users[userIndex]
}

async delete(id: number): Promise<boolean> {
const initialLength = this.users.length
this.users = this.users.filter(user => user.id !== id)
return this.users.length < initialLength
}
}

// services/userService.ts
interface UserService {
createUser(data: CreateUserInput): Promise<User>
getUser(id: number): Promise<User>
getAllUsers(): Promise<User[]>
updateUser(id: number, data: UpdateUserInput): Promise<User>
deleteUser(id: number): Promise<boolean>
}

class UserServiceImpl implements UserService {
constructor(private userRepository: UserRepository) {}

async createUser(data: CreateUserInput): Promise<User> {
// 验证邮箱格式
if (!this.isValidEmail(data.email)) {
throw new Error('Invalid email format')
}

// 验证年龄
if (data.age < 18) {
throw new Error('User must be at least 18 years old')
}

return this.userRepository.create(data)
}

async getUser(id: number): Promise<User> {
const user = await this.userRepository.findById(id)
if (!user) {
throw new Error('User not found')
}
return user
}

async getAllUsers(): Promise<User[]> {
return this.userRepository.findAll()
}

async updateUser(id: number, data: UpdateUserInput): Promise<User> {
// 如果更新邮箱,需要验证格式
if (data.email && !this.isValidEmail(data.email)) {
throw new Error('Invalid email format')
}

return this.userRepository.update(id, data)
}

async deleteUser(id: number): Promise<boolean> {
return this.userRepository.delete(id)
}

private isValidEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
}
}

// controllers/userController.ts
interface UserController {
createUser(req: Request, res: Response): Promise<void>
getUser(req: Request, res: Response): Promise<void>
getAllUsers(req: Request, res: Response): Promise<void>
updateUser(req: Request, res: Response): Promise<void>
deleteUser(req: Request, res: Response): Promise<void>
}

class UserControllerImpl implements UserController {
constructor(private userService: UserService) {}

async createUser(req: Request, res: Response): Promise<void> {
try {
const user = await this.userService.createUser(req.body)
res.status(201).json(user)
} catch (error) {
res.status(400).json({ error: error.message })
}
}

async getUser(req: Request, res: Response): Promise<void> {
try {
const user = await this.userService.getUser(Number(req.params.id))
res.json(user)
} catch (error) {
res.status(404).json({ error: error.message })
}
}

async getAllUsers(req: Request, res: Response): Promise<void> {
const users = await this.userService.getAllUsers()
res.json(users)
}

async updateUser(req: Request, res: Response): Promise<void> {
try {
const user = await this.userService.updateUser(Number(req.params.id), req.body)
res.json(user)
} catch (error) {
res.status(400).json({ error: error.message })
}
}

async deleteUser(req: Request, res: Response): Promise<void> {
try {
const success = await this.userService.deleteUser(Number(req.params.id))
res.status(success ? 204 : 404).json({ success })
} catch (error) {
res.status(400).json({ error: error.message })
}
}
}

// app.ts
// 设置依赖注入
const userRepository = new InMemoryUserRepository()
const userService = new UserServiceImpl(userRepository)
const userController = new UserControllerImpl(userService)

// 模拟Express请求
interface Request {
body: any
params: { [key: string]: string }
}

interface Response {
status(code: number): Response
json(data: any): void
}

// 模拟服务器
const mockServer = {
routes: [],
post(path: string, handler: (req: Request, res: Response) => Promise<void>) {
this.routes.push({ method: 'POST', path, handler })
},
get(path: string, handler: (req: Request, res: Response) => Promise<void>) {
this.routes.push({ method: 'GET', path, handler })
},
put(path: string, handler: (req: Request, res: Response) => Promise<void>) {
this.routes.push({ method: 'PUT', path, handler })
},
delete(path: string, handler: (req: Request, res: Response) => Promise<void>) {
this.routes.push({ method: 'DELETE', path, handler })
},

async handleRequest(method: string, path: string, req: Request) {
const route = this.routes.find(r => r.method === method && r.path === path)
if (!route) {
throw new Error('Route not found')
}

const res = {
status: (code: number) => ({
json: (data: any) => console.log(`${code}:`, data)
}),
json: (data: any) => console.log(JSON.stringify(data))
}

await route.handler(req, res)
}
}

// 设置路由
mockServer.post('/users', (req: Request, res: Response) =>
userController.createUser(req, res))

mockServer.get('/users', (req: Request, res: Response) =>
userController.getAllUsers(req, res))

mockServer.get('/users/:id', (req: Request, res: Response) =>
userController.getUser(req, res))

mockServer.put('/users/:id', (req: Request, res: Response) =>
userController.updateUser(req, res))

mockServer.delete('/users/:id', (req: Request, res: Response) =>
userController.deleteUser(req, res))

// 测试
async function testApp() {
console.log('Creating user...')
await mockServer.handleRequest('POST', '/users', {
body: { name: '张三', email: 'zhangsan@example.com', age: 25 },
params: {}
})

console.log('\nGetting all users...')
await mockServer.handleRequest('GET', '/users', {
body: {},
params: {}
})

console.log('\nGetting user by ID...')
await mockServer.handleRequest('GET', '/users/1', {
body: {},
params: { id: '1' }
})
}

testApp()

案例2:React + TypeScript组件

// components/UserList.tsx
import React, { useState, useEffect } from 'react'

interface User {
id: number
name: string
email: string
age: number
}

interface UserListProps {
initialUsers?: User[]
onUserSelect?: (user: User) => void
}

const UserList: React.FC<UserListProps> = ({
initialUsers = [],
onUserSelect
}) => {
const [users, setUsers] = useState<User[]>(initialUsers)
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)

useEffect(() => {
fetchUsers()
}, [])

const fetchUsers = async () => {
setLoading(true)
setError(null)

try {
const response = await fetch('https://jsonplaceholder.typicode.com/users')
if (!response.ok) {
throw new Error('Failed to fetch users')
}
const data: User[] = await response.json()
setUsers(data)
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error')
} finally {
setLoading(false)
}
}

const handleUserSelect = (user: User) => {
onUserSelect?.(user)
}

if (loading) {
return <div>Loading...</div>
}

if (error) {
return <div>Error: {error}</div>
}

return (
<div className="user-list">
<h2>User List</h2>
<button onClick={fetchUsers} disabled={loading}>
Refresh
</button>

<ul>
{users.map(user => (
<li key={user.id} onClick={() => handleUserSelect(user)}>
<strong>{user.name}</strong>
<br />
{user.email}
<br />
Age: {user.age}
</li>
))}
</ul>
</div>
)
}

export default UserList

// components/UserForm.tsx
import React, { useState } from 'react'

interface UserFormData {
name: string
email: string
age: number
}

interface UserFormProps {
onSubmit: (userData: UserFormData) => void
initialData?: Partial<UserFormData>
}

const UserForm: React.FC<UserFormProps> = ({
onSubmit,
initialData = {}
}) => {
const [formData, setFormData] = useState<UserFormData>({
name: initialData.name || '',
email: initialData.email || '',
age: initialData.age || 0
})

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target
setFormData(prev => ({
...prev,
[name]: name === 'age' ? Number(value) : value
}))
}

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
onSubmit(formData)
}

return (
<form onSubmit={handleSubmit} className="user-form">
<div>
<label>Name:</label>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
required
/>
</div>

<div>
<label>Email:</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
required
/>
</div>

<div>
<label>Age:</label>
<input
type="number"
name="age"
value={formData.age}
onChange={handleChange}
min="0"
required
/>
</div>

<button type="submit">Submit</button>
</form>
)
}

export default UserForm

// pages/UsersPage.tsx
import React, { useState } from 'react'
import UserList from '../components/UserList'
import UserForm from '../components/UserForm'

interface User {
id: number
name: string
email: string
age: number
}

const UsersPage: React.FC = () => {
const [selectedUser, setSelectedUser] = useState<User | null>(null)
const [users, setUsers] = useState<User[]>([])
const [showForm, setShowForm] = useState(false)

const handleUserSelect = (user: User) => {
setSelectedUser(user)
}

const handleUserSubmit = async (userData: { name: string; email: string; age: number }) => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData),
})

if (!response.ok) {
throw new Error('Failed to create user')
}

const newUser: User = {
id: Date.now(),
...userData
}

setUsers(prev => [...prev, newUser])
setShowForm(false)
} catch (error) {
console.error('Error creating user:', error)
}
}

return (
<div className="users-page">
<h1>Users Management</h1>

<div className="controls">
<button onClick={() => setShowForm(!showForm)}>
{showForm ? 'Hide Form' : 'Add User'}
</button>
</div>

{showForm && (
<div className="form-container">
<h2>Add New User</h2>
<UserForm onSubmit={handleUserSubmit} />
</div>
)}

<UserList
initialUsers={users}
onUserSelect={handleUserSelect}
/>

{selectedUser && (
<div className="user-detail">
<h2>Selected User</h2>
<p><strong>Name:</strong> {selectedUser.name}</p>
<p><strong>Email:</strong> {selectedUser.email}</p>
<p><strong>Age:</strong> {selectedUser.age}</p>
</div>
)}
</div>
)
}

export default UsersPage

// utils/validation.ts
export interface ValidationError {
field: string
message: string
}

export interface UserValidationSchema {
name: {
required: boolean
minLength: number
maxLength: number
}
email: {
required: boolean
pattern: RegExp
}
age: {
required: boolean
min: number
max: number
}
}

export const userValidationSchema: UserValidationSchema = {
name: {
required: true,
minLength: 2,
maxLength: 50
},
email: {
required: true,
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
},
age: {
required: true,
min: 0,
max: 150
}
}

export function validateUser(data: Partial<UserValidationSchema>): ValidationError[] {
const errors: ValidationError[] = []

Object.entries(data).forEach(([field, rules]) => {
const value = data[field as keyof typeof data]

if (rules.required && !value) {
errors.push({
field,
message: `${field} is required`
})
return
}

switch (field) {
case 'name':
if (value && value.length < rules.minLength) {
errors.push({
field,
message: `Name must be at least ${rules.minLength} characters`
})
}
if (value && value.length > rules.maxLength) {
errors.push({
field,
message: `Name must be at most ${rules.maxLength} characters`
})
}
break

case 'email':
if (value && !rules.pattern.test(value)) {
errors.push({
field,
message: 'Invalid email format'
})
}
break

case 'age':
if (value && (value < rules.min || value > rules.max)) {
errors.push({
field,
message: `Age must be between ${rules.min} and ${rules.max}`
})
}
break
}
})

return errors
}

十、配置和工具

tsconfig.json

{
"compilerOptions": {
// 目标JavaScript版本
"target": "es2020",

// 模块系统
"module": "esnext",
"lib": ["es2020", "dom"],

// 严格类型检查
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,

// 模块解析
"moduleResolution": "node",
"baseUrl": "./src",
"paths": {
"@/*": ["*"],
"@/components/*": ["components/*"]
},

// 源映射
"sourceMap": true,
"inlineSources": true,
"declaration": true,
"declarationMap": true,

// 额外检查
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,

// JSX支持
"jsx": "react-jsx",
"jsxImportSource": "react",

// 装饰器
"experimentalDecorators": true,
"emitDecoratorMetadata": true,

// 高级功能
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,

// 其他
"skipLibCheck": true,
"incremental": true,
"tsBuildInfoFile": "./dist/.tsbuildinfo"
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist",
"**/*.test.ts"
]
}

工具配置

// package.json
{
"scripts": {
"build": "tsc",
"build:watch": "tsc --watch",
"start": "ts-node src/server.ts",
"test": "jest",
"test:watch": "jest --watch",
"lint": "eslint src --ext .ts,.tsx",
"lint:fix": "eslint src --ext .ts,.tsx --fix"
},
"devDependencies": {
"@types/jest": "^29.5.0",
"@types/node": "^18.15.0",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
"eslint": "^8.37.0",
"jest": "^29.5.0",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.0",
"typescript": "^5.0.0"
}
}

// .eslintrc.json
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"@typescript-eslint/recommended"
],
"rules": {
"@typescript-eslint/explicit-function-return-type": "error",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/prefer-const": "error"
}
}

十一、最佳实践

1. 类型定义最佳实践

// ✅ 好的做法
interface User {
id: number
name: string
email: string
createdAt: Date
updatedAt: Date
}

// ✅ 使用联合类型而不是字符串字面量
type Status = 'active' | 'inactive' | 'pending'

// ✅ 使用接口而不是类型别名(当需要扩展时)
interface Animal {
name: string
age: number
}

interface Dog extends Animal {
breed: string
}

// ✅ 泛型约束
interface HasId {
id: number
}

function findById<T extends HasId>(items: T[], id: number): T | null {
return items.find(item => item.id === id) || null
}

// ❌ 避免的做法
type User = {
id: number
name: string
email: string
createdAt: Date
updatedAt: Date
}

// ❌ 过于宽泛的类型
let data: any // 应该指定具体类型

// ❌ 滥用类型断言
let value = someValue as string // 应该使用类型保护

2. 代码组织

// ✅ 按功能模块组织
// src/
// types/
// user.ts
// product.ts
// services/
// userService.ts
// productService.ts
// utils/
// validation.ts
// dateHelper.ts

// ✅ 类型导出集中管理
// src/types/index.ts
export * from './user'
export * from './product'
export * from './api'

// ✅ 接口实现分离
interface UserRepository {
findById(id: number): Promise<User | null>
}

class InMemoryUserRepository implements UserRepository {
// 实现
}

// ❌ 避免的做法
// 在同一文件中混合类型定义和实现

3. 错误处理

// ✅ 自定义错误类型
class ValidationError extends Error {
constructor(
message: string,
public field: string
) {
super(message)
this.name = 'ValidationError'
}
}

class NotFoundError extends Error {
constructor(resource: string) {
super(`${resource} not found`)
this.name = 'NotFoundError'
}
}

// ✅ 使用Result模式
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E }

function parseAge(age: string): Result<number, ValidationError> {
const num = parseInt(age, 10)
if (isNaN(num)) {
return {
success: false,
error: new ValidationError('Invalid age format', 'age')
}
}
return { success: true, data: num }
}

4. 性能优化

// ✅ 使用类型断言避免重复计算
function processItems(items: Item[]): ProcessedItem[] {
const processedItems: ProcessedItem[] = []

for (const item of items) {
// 避免重复类型检查
if (isProcessedItem(item)) {
processedItems.push(item)
continue
}

// 处理未处理的项
const processed = processItem(item)
processedItems.push(processed)
}

return processedItems
}

// ✅ 使用泛型类型避免重复定义
interface BaseEntity {
id: number
createdAt: Date
updatedAt: Date
}

interface User extends BaseEntity {
name: string
email: string
}

interface Product extends BaseEntity {
name: string
price: number
}

// ✅ 使用Partial和Pick简化类型操作
type UserUpdate = Pick<User, 'name' | 'email'> & Partial<User>

十二、常见问题解答

Q1: 什么时候使用接口,什么时候使用类型别名?

使用接口的场景:

  • 需要扩展或实现
  • 对象类型定义
  • 类实现
interface Animal {
name: string
}

interface Dog extends Animal {
breed: string
}

class Labrador implements Dog {
name = "Buddy"
breed = "Labrador"
}

使用类型别名的场景:

  • 联合类型
  • 交叉类型
  • 条件类型
  • 简单类型定义
type Status = 'active' | 'inactive' | 'pending'
type User = { name: string } & { email: string }
type ExtractType<T> = T extends string ? string : number

Q2: 如何处理第三方库的类型定义?

// 安装类型定义
npm install --save-dev @types/lodash

// 编写声明文件
// src/@types/lodash/index.ts
declare module 'lodash' {
export function debounce<F extends (...args: any[]) => any>(
func: F,
wait?: number,
options?: { leading?: boolean; maxWait?: number }
): F & { cancel(): void }

export function throttle<F extends (...args: any[]) => any>(
func: F,
wait?: number,
options?: { leading?: boolean; trailing?: boolean }
): F
}

// 在tsconfig.json中包含类型定义文件
{
"include": ["src/@types/**/*.d.ts"]
}

Q3: 如何处理动态导入的类型?

// 使用动态导入和类型守卫
async function loadComponent() {
const module = await import('./component')

if (module.default) {
const Component = module.default as React.ComponentType
return <Component />
}

throw new Error('Component not found')
}

// 使用类型断言
async function fetchData(): Promise<User[]> {
const response = await fetch('/api/users')
const data = await response.json()

// 类型断言确保数据类型正确
return data as User[]
}

Q4: 如何处理可选链和空值合并?

// 可选链
const user = {
name: '张三',
address: {
street: '人民路123号',
city: '北京'
}
}

const city = user.address?.city // 如果address不存在,返回undefined

// 空值合并
const name = user.name ?? '匿名用户' // 如果user.name为null或undefined,使用默认值

// 更复杂的类型守卫
function isUser(obj: any): obj is User {
return obj &&
typeof obj.name === 'string' &&
typeof obj.email === 'string'
}

function processUser(user: unknown) {
if (isUser(user)) {
// user的类型被推断为User
console.log(user.name, user.email)
} else {
console.log('Invalid user data')
}
}

十三、总结

TypeScript为JavaScript带来了类型安全性和更好的开发体验。通过本文的学习,你应该掌握了TypeScript的核心概念和最佳实践。

关键要点:

  1. 类型系统是TypeScript的核心,包括基础类型、高级类型、接口、类等
  2. 泛型提供了代码复用的强大能力,让类型更加灵活
  3. 装饰器为元编程提供了支持,可以扩展类的行为
  4. 模块系统支持代码的组织和复用,包括命名空间和动态导入
  5. 配置工具让TypeScript适应不同的项目需求,包括tsconfig.json和各种工具配置

学习建议:

  1. 从小项目开始,逐步使用TypeScript重构现有JavaScript代码
  2. 阅读官方文档,TypeScript的官方文档非常详细和权威
  3. 使用TypeScript Playground,在线试运行代码
  4. 参与社区,学习其他开发者的最佳实践

记住,TypeScript是一个工具,它的目标是帮助你写出更安全、更可维护的代码。不要过度使用复杂的类型定义,保持代码的简洁和可读性。

如果你在使用TypeScript的过程中遇到任何问题,欢迎在评论区留言交流。祝学习愉快!🎉


最后更新:2026年5月14日
分类:TypeScript | JavaScript类型系统 | 前端开发 | 类型安全 | 工程化