引言
TypeScript作为JavaScript的超集,已经成为现代前端开发的标配。它提供的静态类型系统不仅能帮助我们在编码阶段发现错误,更能显著提升代码的可维护性和开发体验。然而,很多开发者仅仅停留在基础使用层面,未能充分发挥TypeScript的强大威力。
本文将带你从TypeScript的基础概念出发,通过20个精心设计的实战示例,逐步深入TypeScript的高级特性。无论你是刚刚接触TypeScript的新手,还是希望提升技能的中级开发者,这篇文章都将为你提供实用的技巧和最佳实践。
入门篇:基础概念与核心技巧
示例1:基础类型定义与类型推断
typescript
// 显式类型声明
let username: string = 'John';
let age: number = 25;
let isActive: boolean = true;
// TypeScript类型推断
let inferredString = 'Hello'; // 自动推断为string类型
let inferredNumber = 42; // 自动推断为number类型
// 数组类型
let numbers: number[] = [1, 2, 3];
let strings: Array<string> = ['a', 'b', 'c'];
示例2:接口定义与对象类型
typescript
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()
};
// user.createdAt = new Date(); // 错误:只读属性不能修改
示例3:函数类型与参数校验
typescript
// 函数类型声明
function greet(name: string, age?: number): string {
return age ? `Hello ${name}, you are ${age} years old!` : `Hello ${name}!`;
}
// 箭头函数类型
const add = (a: number, b: number): number => a + b;
// 函数重载
function processInput(input: string): string;
function processInput(input: number): number;
function processInput(input: string | number): string | number {
return typeof input === 'string' ? input.toUpperCase() : input * 2;
}
示例4:类的基本使用与继承
typescript
class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0): void {
console.log(`${this.name} moved ${distance}m.`);
}
}
class Dog extends Animal {
private breed: string;
constructor(name: string, breed: string) {
super(name);
this.breed = breed;
}
bark(): void {
console.log('Woof! Woof!');
}
}
const dog = new Dog('Buddy', 'Golden Retriever');
dog.bark();
dog.move(10);
示例5:类型断言与类型保护
typescript
// 类型断言
const someValue: any = 'this is a string';
const strLength: number = (someValue as string).length;
// 类型保护
function isString(value: any): value is string {
return typeof value === 'string';
}
function processValue(value: string | number) {
if (isString(value)) {
console.log(value.toUpperCase()); // 类型收窄为string
} else {
console.log(value.toFixed(2)); // 类型收窄为number
}
}
进阶篇:中级应用与设计模式
示例6:泛型的基本使用
typescript
// 泛型极简函数
function identity<T>(arg: T): T {
return arg;
}
// 泛型接口
interface GenericResponse<T> {
data: T;
status: number;
message: string;
}
// 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
const numberInstance = new GenericNumber<number>();
numberInstance.zeroValue = 0;
numberInstance.add = (x, y) => x + y;
示例7:联合类型与交叉类型
typescript
// 联合类型
type Status = 'active' | 'inactive' | 'pending';
type ID = string | number;
// 交叉类型
interface Named {
name: string;
}
interface Aged {
age: number;
}
type Person = Named & Aged;
const person: Person = {
name: 'John',
age: 30
};
// 类型守卫
function isPerson(obj: any): obj is Person {
return obj && typeof obj.name === 'string' && typeof obj.age === 'number';
}
示例8:类型别名与字面量类型
typescript
// 类型别名
type Point = {
x: number;
y: number;
};
type Coordinates = [number, number];
// 字面量类型
type Direction = 'north' | 'south' | 'east' | 'west';
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
// 模板字面量类型
type EventName = `on${string}`;
const event: EventName = 'onClick'; // 正确
// const invalidEvent: EventName = 'click'; // 错误
示例9:枚举类型的高级用法
typescript
// 数字枚举
enum StatusCode {
OK = 200,
Created = 201,
BadRequest = 400,
Unauthorized = 401,
NotFound = 404
}
// 字符串枚举
enum UserRole {
Admin = 'ADMIN',
User = 'USER',
Guest = 'GUEST'
}
// 常量枚举(编译时会被内联)
const enum LogLevel {
Error,
Warn,
Info,
Debug
}
// 使用示例
function handleResponse(code: StatusCode): void {
switch (code) {
case StatusCode.OK:
console.log('请求成功');
break;
case StatusCode.NotFound:
console.log('资源未找到');
break;
}
}
示例10:模块导入导出最佳实践
typescript
// utils.ts
export const PI = 3.14159;
export function calculateArea(radius: number): number {
return PI * radius * radius;
}
export interface Circle {
radius: number;
area: number;
}
// 默认导出
export default class Calculator {
static add(a: number, b: number): number {
return a + b;
}
}
// main.ts
import Calculator, { PI, calculateArea, type Circle } from './utils';
const area = calculateArea(5);
const sum = Calculator.add(10, 20);
示例11:声明文件与第三方库集成
typescript
// 为没有类型定义的库创建声明文件
declare module 'untyped-library' {
export function doSomething(value: string): void;
export const version: string;
}
// 扩展第三方库的类型
declare module 'axios' {
interface AxiosRequestConfig {
retry?: number;
timeout?: number;
}
}
// 全局类型声明
declare global {
interface Window {
MY_APP_CONFIG: {
apiUrl: string;
environment: string;
};
}
}
示例12:可选链与空值合并运算符
typescript
interface User {
name: string;
address?: {
street?: string;
city?: string;
zipCode?: string;
};
contacts?: {
email?: string;
phone?: string;
}[];
}
const user:User = {
name: 'Alice'
};
// 可选链操作符
const city = user.address?.city; // undefined,而不是抛出错误
const firstEmail = user.contacts?.[0]?.email; // 安全访问嵌套属性
// 空值合并运算符
const displayName = user.name ?? 'Unknown'; // 只在null/undefined时使用默认值
const phoneNumber = user.contacts?.[0]?.phone ?? 'No phone';
// 组合使用
const userCity = user.address?.city ?? 'Unknown city';
精通篇:高级特性与最佳实践
示例13:条件类型与类型编程
typescript
// 基础条件类型
type IsString<T> = T extends string ? true : false;
type A = IsString<'hello'>; // true
type B = IsString<number>; // false
// 提取数组元素类型
type ArrayElement<T> = T extends (infer U)[] ? U : never;
type Numbers = ArrayElement<number[]>; // number
// 排除null和undefined
type NonNullable<T> = T extends null | undefined ? never : T;
type ValidString = NonNullable<string | null>; // string
// 递归条件类型
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
interface UserProfile {
name: string;
preferences: {
theme: string;
notifications: boolean;
};
}
type ReadonlyProfile = DeepReadonly<UserProfile>;
示例14:映射类型与实用类型工具
typescript
// 基础映射类型
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Required<T> = {
[P in keyof T]-?: T[P];
};
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// 键重映射
type Getters<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
interface User {
name: string;
age: number;
}
type UserGetters = Getters<User>;
// {
// getName: () => string;
// getAge: () => number;
// }
// 条件键过滤
type Methods<T> = {
[P in keyof T as T[P] extends Function ? P : never]: T[P];
};
class Example {
value: string = '';
method() {}
anotherMethod() {}
}
type ExampleMethods = Methods<Example>; // { method: () => void; anotherMethod: () => void }
示例15:模板字面量类型与模式匹配
typescript
// 基础模板字面量类型
type EventType = `on${'Click' | 'Change' | 'Submit'}`;
// "onClick" | "onChange" | "onSubmit"
// 高级模式匹配
type ExtractEventName<T> = T extends `on${infer U}` ? U : never;
type EventName = ExtractEventName<'onClick'>; // "Click"
// CSS单位类型
type CSSUnit = 'px' | 'em' | 'rem' | '%';
type CSSValue = `${number}${CSSUnit}`;
const width: CSSValue = '100px'; // 正确
// const invalidWidth: CSSValue = '100'; // 错误
// API路由类型安全
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type ApiRoute = `/api/${string}/${HttpMethod}`;
const route: ApiRoute = '/api/users/GET'; // 正确
// const invalidRoute: ApiRoute = '/users/GET'; // 错误
示例16:装饰器与元编程
typescript
// 类装饰器
function Logger<T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
constructor(...args: any[]) {
super(...args);
console.log(`创建了 ${constructor.name} 的实例`);
}
};
}
// 方法装饰器
function LogMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`调用方法: ${propertyKey}`, args);
return originalMethod.apply(this, args);
};
return descriptor;
}
// 属性装饰器
function DefaultValue(value: any) {
return function (target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
get() {
return this[`_${propertyKey}`] ?? value;
},
set(newValue) {
this[`_${propertyKey}`] = newValue;
},
enumerable: true,
configurable: true
});
};
}
@Logger
class ExampleClass {
@DefaultValue('default')
name: string;
@LogMethod
greet(message: string): string {
return `Hello, ${this.name}! ${message}`;
}
}
示例17:高级类型推断技巧
typescript
// infer关键字深度使用
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
// 函数重载推断
function createArray<T>(length: number, value: T): T[];
function createArray<T>(length: number): T[];
function createArray<T>(length: number, value?: T): T[] {
return Array.from({ length }, () => value as T);
}
// 条件类型分发
type DistributedCondition<T> = T extends any ? T[] : never;
type Distributed = DistributedCondition<string | number>; // string[] | number[]
// 避免分发
type NonDistributed<T> = [T] extends [any] ? T[] : never;
type NonDistributedResult = NonDistributed<string | number>; // (string | number)[]
示例18:TSConfig配置优化指南
json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"moduleResolution": "node",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"removeComments": false,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@/components/*": ["src/components/*"],
"@/utils/*": ["src/utils/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}
示例19:第三方库类型扩展实战
typescript
// 扩展React组件props
import React from 'react';
declare module 'react' {
interface ButtonHTMLAttributes<T> {
variant?: 'primary' | 'secondary' | 'danger';
size?: 'sm' | 'md' | 'lg';
}
}
// 扩展Express请求对象
import { Request } from 'express';
declare global {
namespace Express {
interface Request {
user?: {
id: string;
email: string;
role: string;
};
correlationId?: string;
}
}
}
// 自定义环境变量类型安全
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_APP_NAME: string;
readonly VITE_DEBUG: boolean;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
示例20:复杂类型工具实现
typescript
// 深度Partial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// 深度Required
type DeepRequired<T> = {
[P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];
};
// 类型安全的Object.keys
const objectKeys = <T extends object>(obj: T): (key of T)[] => {
return Object.keys(obj) as (keyof T)[];
};
// 类型安全的fetch包装器
async function typedFetch<T>(
url: string,
options?: RequestInit
): Promise<{ data: T; status: number }> {
const response = await fetch(url, options);
const data = await response.json();
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return { data: data as T, status: response.status };
}
// 使用示例
interface User {
id: number;
name: string;
email: string;
}
const getUser = async (id: number) => {
const result = await typedFetch<User>(`/api/users/${id}`);
return result.data; // 类型安全的User
};
特别篇:为无类型npm包自定义类型声明
实战案例:为simple-utils
包创建类型声明
假设我们有一个没有类型定义的npm包simple-utils
,包含以下功能:
javascript
// 假设的simple-utils包内容
export function formatDate(date, format = 'YYYY-MM-DD') {
// 实现日期格式化
}
export function debounce(fn, delay) {
// 防抖实现
}
export const version = '1.2.3';
方法一:创建全局声明文件
typescript
// types/simple-utils.d.ts
declare module 'simple-utils' {
export function formatDate(
date: Date | string | number,
format?: string
): string;
export function debounce<T extends (...args: any[]) => any>(
fn: T,
delay: number
): (...args: Parameters<T>) => void;
export const version: string;
}
方法二:使用模块增强
typescript
// src/types/simple-utils.ts
declare module 'simple-utils' {
interface FormatOptions {
locale?: string;
timezone?: string;
}
export function formatDate(
date: Date | string | number,
format?: string,
options?: FormatOptions
): string;
export function debounce<T extends (...args: any[]) => any>(
fn: T,
delay: number,
immediate?: boolean
): {
(...args: Parameters<T>): void;
cancel(): void;
};
export const version: string;
}
方法三:完整的类型定义示例
typescript
// types/simple-utils/index.d.ts
declare module 'simple-utils' {
export interface FormatDateOptions {
/**
* 区域设置,默认为'en-US'
*/
locale?: string;
/**
* 时区,默认为系统时区
*/
timezone?: string;
/**
* 是否显示时间
*/
showTime?: boolean;
}
export interface DebounceOptions {
/**
* 是否立即执行
*/
immediate?: boolean;
/**
* 最大等待时间
*/
maxWait?: number;
}
/**
* 格式化日期
* @param date 日期对象、时间戳或日期字符串
* @param format 格式字符串,默认为'YYYY-MM-DD'
* @param options 格式化选项
* @returns 格式化后的日期字符串
*/
export function formatDate(
date: Date | string | number,
format?: string,
options?: FormatDateOptions
): string;
/**
* 防抖函数
* @param fn 要防抖的函数
* @param delay 延迟时间(毫秒)
* @param options 防抖选项
* @returns 防抖后的函数,包含cancel方法
*/
export function debounce<T extends (...args: any[]) => any>(
fn: T,
delay: number,
options?: DebounceOptions
): {
(...args: Parameters<T>): void;
cancel(): void;
flush(): void;
};
/**
* 节流函数
*/
export function throttle<T extends (...args: any[]) => any>(
fn: T,
delay: number,
options?: {
leading?: boolean;
trailing?: boolean;
}
): (...args: Parameters<T>) => void;
export const version: string;
export const author: string;
}
方法四:在项目中配置使用
json
// tsconfig.json
{
"compilerOptions": {
"typeRoots": ["./node_modules/@types", "./types"],
"types": ["simple-utils"]
},
"include": ["src/**/*", "types/**/*"]
}
方法五:发布到DefinitelyTyped
如果你的类型定义足够完善,可以考虑发布到@types组织:
bash
# 创建类型定义包
npm init --scope=types simple-utils
# 安装发布工具
npm install -g dts-gen
# 生成类型定义模板
dts-gen -m simple-utils
使用示例
typescript
import { formatDate, debounce } from 'simple-utils';
// 现在有完整的类型提示和校验
const formatted = formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss', {
locale: 'zh-CN'
});
const debouncedSearch = debounce((query: string) => {
console.log('Searching for:', query);
}, 300);
// 调用防抖函数
debouncedSearch('typescript');
总结
通过这20个精心设计的TypeScript示例,我们从基础类型定义一路深入到高级类型编程和元编程技巧。TypeScript的强大之处在于它既能提供JavaScript的灵活性,又能通过静态类型系统带来编译时的安全保障。
关键要点回顾:
- 基础要扎实:掌握类型推断、接口、类等核心概念
- 进阶要深入:泛型、联合类型、条件类型是提升代码质量的关键
- 高级要精通:类型编程、装饰器、配置优化能显著提升开发效率
- 无类型包处理:掌握为第三方库创建类型声明的各种方法
- 实践出真知:每个技巧都要在实际项目中应用和验证
TypeScript的学习是一个持续的过程,建议你将这些技巧应用到实际项目中,逐步培养类型思维。记住,好的TypeScript代码不是一味地添加类型注解,而是通过类型系统来表达代码的意图和约束。
开始你的TypeScript精通之旅吧