TypeScript 全面指南:类型系统与工程实践
TypeScript 基础入门
TypeScript 是 JavaScript 的超集,它在 JS 的基础上增加了静态类型系统,让大型项目的开发更加安全和高效。本指南将从零开始,带你掌握 TypeScript 的核心类型系统与现代工程实践。
环境搭建与第一个 TypeScript 文件
在开始之前,请确保已安装 Node.js。全局安装 TypeScript 编译器:
npm install -g typescript
创建一个 hello.ts 文件:
function greet(name: string): string {
return `Hello, ${name}!`;
}
console.log(greet("World"));
运行 tsc hello.ts 会生成 hello.js。可以用 node hello.js 执行,或直接使用 ts-node hello.ts (需额外安装 ts-node)。
基本类型标注
TypeScript 的类型标注写在变量、参数、返回值后面,使用 : type 语法。
let isDone: boolean = false;
let age: number = 25;
let firstName: string = "Alice";
let list: number[] = [1, 2, 3];
let tuple: [string, number] = ["Bob", 30]; // 元组
let anything: any = "could be anything";
函数类型
可以标注参数类型和返回值类型。返回值类型可省略,TS 会自动推断,但显式标注让意图更清晰。
function add(x: number, y: number): number {
return x + y;
}
// 可选参数与默认参数
function buildName(first: string, last?: string): string {
return last ? `${first} ${last}` : first;
}
接口(Interface)
接口用于定义对象的形状,是 TypeScript 类型系统的核心。
interface User {
name: string;
age: number;
email?: string; // 可选属性
readonly id: number; // 只读属性
}
function printUser(user: User): void {
console.log(`${user.name} is ${user.age} years old`);
}
类型别名与联合类型
使用 type 关键字可以创建类型别名,联合类型表示一个值可以是几种类型之一。
type ID = string | number;
function getRecord(id: ID): object {
// ...
}
type Result = "success" | "error" | "pending"; // 字面量联合类型
高级类型系统
随着应用的复杂化,TypeScript 的类型系统展现出强大的威力。以下内容将带你深入理解泛型、类型守卫、映射类型等高级特性。
泛型(Generics)
泛型让组件(函数、类、接口)能够支持多种类型,同时保持类型安全。
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("myString"); // 显式指定
let inferred = identity(42); // 类型推断
// 泛型约束
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): T {
console.log(arg.length);
return arg;
}
类型守卫与类型缩小
通过条件判断,TypeScript 可以在代码块中自动缩小变量的具体类型。
function isString(value: unknown): value is string {
return typeof value === "string";
}
function process(input: string | number) {
if (isString(input)) {
// 这里 input 被收窄为 string
console.log(input.toUpperCase());
} else {
// 这里 input 是 number
console.log(input.toFixed(2));
}
}
映射类型与实用工具类型
TypeScript 内置了许多实用工具类型(Utility Types),如 Partial、Required、Readonly、Pick、Record 等,它们基于映射类型实现。
interface Todo {
title: string;
description: string;
completed: boolean;
}
type PartialTodo = Partial<Todo>; // 所有属性变为可选
type TodoPreview = Pick<Todo, "title" | "completed">; // 仅挑选部分属性
// 自定义映射类型:把每个属性变为可空
type Nullable<T> = { [K in keyof T]: T[K] | null };
type NullableTodo = Nullable<Todo>;
条件类型与 infer
条件类型根据条件选择不同的类型,配合 infer 可以提取类型信息。
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<string>; // "yes"
type B = IsString<number>; // "no"
// 提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type Func = (x: number) => string;
type Result = ReturnType<Func>; // string
TypeScript 工程实践
类型系统不仅停留在代码层面,更是工程化开发的基石。合理配置工具链、选择模块化策略,能让 TypeScript 项目保持长期可维护。
tsconfig.json 配置精解
tsconfig.json 是 TypeScript 项目的核心配置文件。一个典型的项目配置如下:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"sourceMap": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
严格模式 (strict: true) 会启用所有严格类型检查选项,包括 noImplicitAny、strictNullChecks 等,强烈推荐开启。
模块化与声明文件
TypeScript 支持 ES Module 和 CommonJS 两种模块语法。对于非 TS 编写的第三方库,需要引入声明文件(.d.ts)来获取类型信息。
// 导入外部类型
import lodash from "lodash";
// DefinitelyTyped 提供了大量库的声明文件 @types/xxx
// 安装:npm i --save-dev @types/lodash
// 自定义声明文件(globals.d.ts)
declare module "*.svg" {
const content: string;
export default content;
}
declare const VERSION: string; // 全局变量声明
在 React 项目中使用 TypeScript
React + TypeScript 是当前主流组合。组件属性的类型定义如下:
import React from "react";
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
}
const Button: React.FC<ButtonProps> = ({ label, onClick, disabled = false }) => {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
};
React 自带泛型类型,例如 React.FC、React.Component,以及事件类型如 React.ChangeEvent<HTMLInputElement>。
构建工具集成
实际项目通常使用 Webpack、Vite 或 esbuild 进行打包。以 Vite 为例,创建项目时选择 TypeScript 模板即可:
npm create vite@latest my-app -- --template react-ts
Vite 内置了 esbuild 转译 TypeScript,类型检查需要额外运行 tsc --noEmit,可在脚本中同时执行。
代码质量与协作规范
- ESLint + @typescript-eslint:提供 TS 专用规则,防止可疑代码。
- Prettier:统一代码风格。
- 类型优先的开发模式:先定义接口,再实现功能,让类型指导开发。
- 禁止滥用
any:尽量使用具体类型或unknown,并在适用场景使用as类型断言时必须谨慎。
单元测试与类型测试
测试框架如 Jest 配合 ts-jest 可直接运行 .ts 测试文件。类型层面的测试可使用 tsd 或 expect-type。
// 使用 expect-type 进行编译时断言
import { expectTypeOf } from "expect-type";
import { add } from "./math";
expectTypeOf(add).parameter(0).toBeNumber();
expectTypeOf(add).returns.toBeNumber();
总结与学习路径
TypeScript 的类型系统既灵活又强大,掌握它需要循序渐进:
- 基础类型与函数:熟悉基本标注、接口、联合类型。
- 高级类型:深入理解泛型、条件类型、映射类型,能编写可复用的类型工具。
- 工程配置:能搭建 TS 项目、配置严格模式、集成构建工具。
- 框架实践:在 React、Node.js 等环境中游刃有余地使用类型。
- 代码规范:借助 ESLint、Prettier 保持代码质量,形成类型驱动的开发思维。
持续实践是关键。尝试将现有 JavaScript 项目逐步迁移至 TypeScript,感受类型系统带来的安全感与开发效率提升。