TypeScript 概述
为什么需要 TypeScript
┌──────────────────────────────────────────────────────────────┐
│ JavaScript 类型问题 │
├──────────────────────────────────────────────────────────────┤
│ │
│ JavaScript: 动态类型,运行时才发现问题 │
│ │
│ function add(a, b) { │
│ return a + b; │
│ } │
│ │
│ add(1, 2) // ✅ 3 │
│ add("a", "b") // ✅ "ab" │
│ add(1, "2") // ⚠️ "12" (隐式转换) │
│ add({}, []) // ⚠️ "[object Object]" │
│ │
├──────────────────────────────────────────────────────────────┤
│ │
│ TypeScript: 静态类型,编译时就发现问题 │
│ │
│ function add(a: number, b: number): number { │
│ return a + b; │
│ } │
│ │
│ add(1, 2) // ✅ │
│ add("a", "b") // ❌ 编译错误! │
│ add(1, "2") // ❌ 编译错误! │
│ │
└──────────────────────────────────────────────────────────────┘
TypeScript 配置
// tsconfig.json
{
"compilerOptions": {
// 目标版本
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
// 模块解析
"moduleResolution": "bundler",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
// 严格模式(必须开启)
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
// 输出控制
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
// 其他
"skipLibCheck": true,
"incremental": true,
"tsBuildInfoFile": ".tsbuildinfo"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
基础类型
原始类型
// 原始类型
const name: string = 'John';
const age: number = 30;
const isActive: boolean = true;
const bigInt: bigint = 100n;
const symbol: symbol = Symbol('id');
// null 和 undefined
const nullValue: null = null;
const undefinedValue: undefined = undefined;
// void(用于函数无返回值)
function log(message: string): void {
console.log(message);
}
// never(用于永不返回的函数)
function throwError(message: string): never {
throw new Error(message);
}
// any(尽量避免使用)
const unknown: any = JSON.parse('{"name": "John"}');
数组和元组
// 数组
const numbers: number[] = [1, 2, 3];
const names: Array<string> = ['a', 'b', 'c'];
// 只读数组
const readonly: ReadonlyArray<number> = [1, 2, 3];
const readonly2: readonly number[] = [1, 2, 3];
// 元组(固定长度和类型的数组)
const tuple: [string, number] = ['age', 30];
const [key, value] = tuple; // 解构赋值
// 可选元素的元组
const optionalTuple: [string, number?] = ['hello'];
// 元组标签
const labeled: [name: string, age: number] = ['John', 30];
对象类型
// 对象类型
interface User {
name: string;
age: number;
email?: string; // 可选属性
readonly id: number; // 只读属性
}
// 类型别名
type User = {
name: string;
age: number;
};
// 索引签名
interface StringMap {
[key: string]: string;
}
const map: StringMap = {
key1: 'value1',
key2: 'value2'
};
// 嵌套对象
interface Address {
street: string;
city: string;
country: string;
zipCode: string;
}
interface Company {
name: string;
address: Address;
}
联合类型与交叉类型
联合类型
// 联合类型:可以是多种类型之一
type StringOrNumber = string | number;
type Result = Success | Error;
type Status = 'pending' | 'fulfilled' | 'rejected';
// 字面量类型
type Direction = 'north' | 'south' | 'east' | 'west';
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
// 使用联合类型
function format(value: string | number): string {
if (typeof value === 'string') {
return value.toUpperCase(); // TypeScript 知道这是 string
}
return value.toFixed(2); // TypeScript 知道这是 number
}
// 类型守卫
type Animal = Dog | Cat | Bird;
function speak(animal: Animal) {
if (animal.type === 'dog') {
animal.bark(); // TypeScript 知道是 Dog
} else if (animal.type === 'cat') {
animal.meow(); // TypeScript 知道是 Cat
} else {
animal.fly(); // TypeScript 知道是 Bird
}
}
交叉类型
// 交叉类型:同时具有多种类型
interface Printable {
print(): void;
}
interface Loggable {
log(): void;
}
// 交叉类型
type PrintAndLog = Printable & Loggable;
const obj: PrintAndLog = {
print() { console.log('print'); },
log() { console.log('log'); }
};
// 混入模式
function withLogging<T extends object>(target: T): T & Loggable {
return {
...target,
log() { console.log('logged'); }
};
}
泛型
基础泛型
// 泛型函数
function identity<T>(value: T): T {
return value;
}
const num = identity<number>(42);
const str = identity('hello'); // TypeScript 自动推断
// 泛型接口
interface Container<T> {
value: T;
get(): T;
set(value: T): void;
}
class Box<T> implements Container<T> {
constructor(private _value: T) {}
get(): T { return this._value; }
set(value: T) { this._value = value; }
}
const box = new Box<number>(123);
泛型约束
// 使用 extends 约束类型
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(value: T): void {
console.log(value.length);
}
logLength('hello'); // ✅ string 有 length
logLength([1, 2, 3]); // ✅ array 有 length
logLength({ length: 10 }); // ✅ 有 length 属性
// logLength(123); // ❌ number 没有 length
// keyof 约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: 'John', age: 30 };
const name = getProperty(user, 'name'); // ✅ string
const age = getProperty(user, 'age'); // ✅ number
// const invalid = getProperty(user, 'email'); // ❌
// 多重约束
function combine<T extends HasLength & HasName, U extends HasName>(
a: T, b: U
): T & U {
return { ...a, ...b };
}
泛型工具类型
// Partial - 所有属性变为可选
interface User {
name: string;
age: number;
email: string;
}
type PartialUser = Partial<User>;
// { name?: string; age?: number; email?: string; }
// Required - 所有属性变为必需
type RequiredUser = Required<User>;
// Readonly - 所有属性变为只读
type ReadonlyUser = Readonly<User>;
// Pick - 选择属性
type UserPreview = Pick<User, 'name' | 'email'>;
// { name: string; email: string; }
// Omit - 排除属性
type UserWithoutAge = Omit<User, 'age'>;
// { name: string; email: string; }
// Record - 创建键值对类型
type UserRoles = Record<string, Role>;
type Permissions = 'read' | 'write' | 'delete';
const permissions: Record<Permissions, boolean> = {
read: true,
write: false,
delete: true
};
// Exclude 和 Extract
type AllStatuses = 'pending' | 'active' | 'inactive' | 'deleted';
type ActiveStatuses = Exclude<AllStatuses, 'pending' | 'deleted'>;
// 'active' | 'inactive'
type ExtractStatuses = Extract<AllStatuses, 'active' | 'pending'>;
// 'pending' | 'active'
// NonNullable - 移除 null 和 undefined
type NonNullEmail = NonNullable<User['email'] | null | undefined>;
// string
// ReturnType - 获取函数返回类型
function createUser() {
return { name: 'John', age: 30 };
}
type UserType = ReturnType<typeof createUser>;
// { name: string; age: number; }
// Parameters - 获取函数参数类型
function updateUser(id: number, name: string, age: number) {}
type UpdateUserParams = Parameters<typeof updateUser>;
// [id: number, name: string, age: number]
高级类型
条件类型
// 基础条件类型
type IsString<T> = T extends string ? 'yes' : 'no';
type A = IsString<string>; // 'yes'
type B = IsString<number>; // 'no'
// 分布式条件类型
type Flatten<T> = T extends Array<infer U> ? U : T;
type C = Flatten<string[]>; // string
type D = Flatten<number>; // number
// 推断类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type E = ReturnType<() => string>; // string
type F = ReturnType<(x: number) => boolean>; // boolean
// 工具类型的实现
type MyParameters<T extends (...args: any) => any> =
T extends (...args: infer P) => any ? P : never;
type MyConstructorParameters<T extends new (...args: any) => any> =
T extends new (...args: infer P) => any ? P : never;
type MyInstanceType<T extends new (...args: any) => any> =
T extends new (...args: any) => infer R ? R : never;
映射类型
// 基础映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Optional<T> = {
[P in keyof T]?: T[P];
};
// 带条件映射
type Concrete<T> = {
[P in keyof T]-?: T[P]; // -? 移除可选
};
type NonNullable<T> = {
[P in keyof T]: NonNullable<T[P]>;
};
// 模板字面量映射
type EventName<T extends string> = `on${Capitalize<T>}`;
type ButtonEvents = EventName<'click' | 'focus' | 'blur'>;
// 'onClick' | 'onFocus' | 'onBlur'
// 递归映射类型
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
模板字面量类型
// 模板字面量
type World = 'world';
type Greeting = `hello ${World}`; // 'hello world'
// 联合类型展开
type Direction = 'north' | 'south' | 'east' | 'west';
type EventName = `on${Direction}`;
type CSSProperty = `${Capitalize<Direction>}Margin`;
// 提取 URL 部分
type ExtractRoute<T extends string> =
T extends `${infer Method} /api/${infer Route}` ? Route : never;
type Route = ExtractRoute<'GET /api/users'>; // 'users'
type Route2 = ExtractRoute<'POST /api/users'>; // 'users'
// type Route3 = ExtractRoute<'GET /other'>; // never
// JSON 类型
type JsonPrimitive = string | number | boolean | null;
type JsonObject = { [key: string]: JsonValue };
type JsonArray = JsonValue[];
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
类型守卫与类型窄缩
自定义类型守卫
interface Cat {
meow(): void;
type: 'cat';
}
interface Dog {
bark(): void;
type: 'dog';
}
type Animal = Cat | Dog;
// 类型守卫函数
function isCat(animal: Animal): animal is Cat {
return animal.type === 'cat';
}
function isDog(animal: Animal): animal is Dog {
return animal.type === 'dog';
}
// 使用
function speak(animal: Animal) {
if (isCat(animal)) {
animal.meow();
} else if (isDog(animal)) {
animal.bark();
}
}
// in 操作符类型守卫
function isCat2(animal: Animal): animal is Cat {
return 'meow' in animal;
}
typeof 类型守卫
function processValue(value: string | number | null) {
if (typeof value === 'string') {
// value 是 string 类型
value.toUpperCase();
} else if (typeof value === 'number') {
// value 是 number 类型
value.toFixed(2);
}
// value 是 null
}
断言函数
// 断言函数 - 确保类型
function assertIsString(val: unknown): asserts val is string {
if (typeof val !== 'string') {
throw new Error('Not a string!');
}
}
function process(value: unknown) {
assertIsString(value);
// 这里 value 被断言为 string
value.toUpperCase();
}
// 非空断言
function getLength(str: string | null): number {
return str!.length; // 确信 str 不为 null
}
// 确定赋值断言
let initialized: string;
// console.log(initialized); // ❌ 可能未初始化
let definitelyInitialized!: string;
console.log(definitelyInitialized); // ✅ 告诉 TS 我确定已赋值
装饰器与反射
装饰器基础(实验性)
// 启用 experimentalDecorators
// tsconfig.json: { "experimentalDecorators": true }
// 类装饰器
function sealed(target: Function) {
Object.seal(target);
Object.seal(target.prototype);
}
@sealed
class BugReport {
id: number;
}
// 方法装饰器
function log(target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${key} with`, args);
return original.apply(this, args);
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number) {
return a + b;
}
}
// 属性装饰器
function readonly(target: any, key: string) {
Object.defineProperty(target, key, {
writable: false,
value: 42
});
}
class Config {
@readonly
VERSION = '1.0.0';
}
TypeScript 与 React
React 组件类型
import React, { FC, useState, useCallback } from 'react';
// Props 类型定义
interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'sm' | 'md' | 'lg';
children: React.ReactNode;
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
disabled?: boolean;
}
// 函数组件
export const Button: FC<ButtonProps> = ({
variant = 'primary',
size = 'md',
children,
onClick,
disabled = false
}) => {
return (
<button
className={`btn btn-${variant} btn-${size}`}
onClick={onClick}
disabled={disabled}
>
{children}
</button>
);
};
// Hooks 类型
interface UseCounterProps {
initial?: number;
min?: number;
max?: number;
}
function useCounter({
initial = 0,
min = Number.MIN_SAFE_INTEGER,
max = Number.MAX_SAFE_INTEGER
}: UseCounterProps = {}) {
const [count, setCount] = useState(initial);
const increment = useCallback(() => {
setCount(c => Math.min(c + 1, max));
}, [max]);
const decrement = useCallback(() => {
setCount(c => Math.max(c - 1, min));
}, [min]);
return { count, increment, decrement, setCount };
}
// 泛型组件
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
keyExtractor: (item: T) => string;
}
export function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
return (
<ul>
{items.map(item => (
<li key={keyExtractor(item)}>
{renderItem(item)}
</li>
))}
</ul>
);
}
事件处理类型
// React 事件类型
function EventExamples() {
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log(e.clientX, e.clientY);
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
console.log('Enter pressed');
}
};
return (
<form onSubmit={handleSubmit}>
<input
onChange={handleChange}
onKeyDown={handleKeyDown}
/>
<button onClick={handleClick}>Click</button>
</form>
);
}
这一章想说的
TypeScript 是大型前端项目的必备工具:
- 类型安全:编译时发现类型错误,减少运行时 bug
- 类型推断:TypeScript 能自动推断类型,减少显式标注
- 泛型:编写可复用且类型安全的代码
- 高级类型:条件类型、映射类型、模板字面量类型
- 类型守卫:缩小类型范围,确保类型安全
合理使用 TypeScript 能显著提升代码质量和可维护性。