TypeScript
类型系统
基础类型
// 基础类型
let isDone: boolean = false;
let count: number = 42;
let decimal: number = 3.14;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let big: bigint = 100n;
let name: string = 'Alice';
let sym: symbol = Symbol('key');
// 数组
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3]; // 泛型写法(JSX 中避免冲突)
// 元组(Tuple)
let tuple: [string, number] = ['hello', 42];
tuple[2] // 访问越界索引默认 undefined(strict 下报错)
// 枚举
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right, // 3
}
enum Status {
Active = 'ACTIVE',
Inactive = 'INACTIVE',
}
// 枚举编译后会有反向映射(常量枚举 const enum 无反向映射)
Direction.Up; // 0
Direction[0]; // "Up"
any / unknown / never / void 区别
// any —— 绕过类型检查(应尽量避免)
let anyVal: any = 42;
anyVal = 'string';
anyVal.foo(); // 无类型保护,运行时可能报错
// unknown —— 安全的 any(使用前必须类型收窄)
let unknownVal: unknown = 42;
unknownVal.toFixed(); // Error: Object is of type 'unknown'
if (typeof unknownVal === 'number') {
unknownVal.toFixed(); // OK(类型收窄后)
}
// never —— 永远不会发生(函数永不返回、穷尽检查)
function throwError(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}
// void —— 函数无返回值
function log(msg: string): void {
console.log(msg);
// 可以 return undefined,但不能 return 值
}
// 类型层级:never < literal < 基础类型 < unknown/any
联合类型 / 交叉类型
// 联合类型(|)—— 值可以是其中一种
type Status = 'loading' | 'success' | 'error';
type Result = number | string;
// 交叉类型(&)—— `所有属性合并
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged; // { name: string; age: number; }
// 交叉类型冲突处理
type A = { a: string };
type B = { a: number };
type C = A & B; // a: never(string & number → never)
字面量类型
// 字面量类型(value as type)
let x: 'hello' = 'hello'; // x 只能是 'hello'
type Direction = 'north' | 'south' | 'east' | 'west';
// 模板字面量类型
type EventName = `on${Capitalize<string>}`;
type CSSValue = `${number}px` | `${number}rem`;
类型断言
// 尖括号(JSX 中不能用)
let someValue: unknown = 'string';
let len: number = (<string>someValue).length;
// as(推荐)
let len2: number = (someValue as string).length;
// 非空断言(!)
let maybeStr: string | null | undefined;
maybeStr!.length; // 断言不为 null/undefined
// 双重断言(尽量避免,破坏 type safety)
let x = 'hello' as unknown as number;
接口与类型别名
interface vs type 区别
// 接口(interface)—— 声明合并 + extends
interface User {
name: string;
}
interface User {
age: number; // 同名合并
}
// User → { name: string; age: number }
interface Admin extends User {
role: 'admin';
}
// 类型别名(type)—— 联合/交叉/元组/映射类型等复杂场景
type ID = string | number;
type Status = 'active' | 'inactive';
type Pair<T> = [T, T];
type Mapped<T> = { [K in keyof T]: boolean };
// 区别总结
// 1. interface 支持声明合并,type 不支持
// 2. type 可以表达联合/交叉/元组/条件类型等,interface 不能
// 3. interface extends 语法更清晰,type 用 &
// 4. 对对象类型优先用 interface,需要联合/映射等用 type
索引签名
interface StringMap {
[key: string]: string; // 所有属性必须是 string 类型
}
interface NumberOrString {
[key: string]: string | number; // 值与索引签名兼容
length: number; // 允许特定的非 string 属性
name: string;
}
// 两种索引(string 和 number)必须兼容
interface Mixed {
[index: number]: string; // 数字签名
[key: string]: string; // 字符串签名(number → string 必须兼容)
}
函数类型
// 方式一:类型别名
type Fn = (x: number, y: number) => number;
// 方式二:接口
interface FnInterface {
(x: number, y: number): number;
}
// 函数重载(多个签名)
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
return a + b;
}
混合类型
// 可调用 + 有属性
interface Counter {
(start: number): string; // 可调用
interval: number; // 有属性
reset(): void;
}
function getCounter(): Counter {
const counter = ((n: number) => 'count: ' + n) as Counter;
counter.interval = 1000;
counter.reset = () => {};
return counter;
}
泛型
泛型基础与约束
// 基础泛型
function identity<T>(arg: T): T {
return arg;
}
identity<string>('hello');
identity(42); // 类型推断
// 泛型约束(extends)
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength('hello'); // OK,string 有 length
logLength([1, 2]); // OK
logLength(42); // Error:number 没有 length
// 多泛型参数
function pair<K, V>(key: K, value: V): [K, V] {
return [key, value];
}
泛型工具类型(内置)
interface User {
id: number;
name: string;
email: string;
age?: number;
}
// Partial<T> —— 所有属性可选
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; age?: number; }
// Required<T> —— 所有属性必填
type RequiredUser = Required<User>;
// { id: number; name: string; email: string; age: number; }
// Readonly<T> —— 所有属性只读
type ReadonlyUser = Readonly<User>;
// Pick<T, K> —— 选取部分属性
type UserInfo = Pick<User, 'id' | 'name'>;
// { id: number; name: string; }
// Omit<T, K> —— 排除部分属性
type UserWithoutEmail = Omit<User, 'email'>;
// { id: number; name: string; age?: number; }
// Record<K, T> —— 构造对象类型
type PageInfo = Record<'home' | 'about' | 'contact', { title: string }>;
// { home: { title: string }; about: { title: string }; contact: { title: string }; }
// Exclude<T, U> —— 从联合类型 T 排除 U
type T0 = Exclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'
// Extract<T, U> —— 从联合类型 T 提取 U
type T1 = Extract<'a' | 'b' | 'c', 'a' | 'f'>; // 'a'
// NonNullable<T> —— 排除 null 和 undefined
type T2 = NonNullable<string | number | null | undefined>; // string | number
// ReturnType<T> —— 获取函数返回类型
type T3 = ReturnType<() => string>; // string
type T4 = ReturnType<<T>() => T>; // unknown
type T5 = ReturnType<typeof fetchData>;
// Parameters<T> —— 获取函数参数类型
type T6 = Parameters<(a: string, b: number) => void>; // [string, number]
// ConstructorParameters<T> —— 构造函数参数类型
class Foo { constructor(a: string, b: number) {} }
type T7 = ConstructorParameters<typeof Foo>; // [string, number]
// InstanceType<T> —— 实例类型
type T8 = InstanceType<typeof Foo>; // Foo
条件类型
// 基础条件类型:T extends U ? X : Y
type IsString<T> = T extends string ? true : false;
type A = IsString<'hello'>; // true
type B = IsString<42>; // false
// 条件类型分配律(Distributive Conditional Types)
// 裸类型参数在条件类型中会被分配
type ToArray<T> = T extends any ? T[] : never;
type C = ToArray<string | number>; // string[] | number[](不是 (string | number)[])
// 阻止分配:用 [] 包裹
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type D = ToArrayNonDist<string | number>; // (string | number)[]
infer 关键字
// infer —— 在条件类型中推断类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type ParamType<T> = T extends (arg: infer P) => any ? P : never;
// 提取 Promise 内部类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type E = UnwrapPromise<Promise<string>>; // string
type F = UnwrapPromise<number>; // number
// 提取数组元素类型
type ArrayItem<T> = T extends (infer U)[] ? U : never;
type G = ArrayItem<string[]>; // string
// infer 递归(取出深层 Promise)
type DeepUnwrap<T> = T extends Promise<infer U> ? DeepUnwrap<U> : T;
type H = DeepUnwrap<Promise<Promise<Promise<string>>>>; // string
模板字面量类型
type EventName = `on${Capitalize<string>}`;
// 'onChange' | 'onClick' | 'onSubmit' ...
// 内置字符串操作类型
type UppercaseName = Uppercase<'hello'>; // 'HELLO'
type LowercaseName = Lowercase<'HELLO'>; // 'hello'
type Capitalized = Capitalize<'hello'>; // 'Hello'
type Uncapitalized = Uncapitalize<'Hello'>; // 'hello'
类
访问修饰符
class Animal {
public name: string; // 默认,任何地方可访问
private age: number; // 仅类内部(编译时检查,运行时无强制)
protected type: string; // 类内部和子类
readonly id: string; // 只读(初始化后不可修改)
#privateField = 42; // ES2022 原生私有字段(运行时有强制)
constructor(name: string, age: number) {
this.name = name;
this.age = age;
this.id = crypto.randomUUID();
}
}
抽象类
abstract class Shape {
abstract area(): number; // 抽象方法(子类必须实现)
describe(): string { // 非抽象方法
return `Area: ${this.area()}`;
}
}
class Circle extends Shape {
constructor(private radius: number) {
super();
}
area(): number {
return Math.PI * this.radius ** 2;
}
}
实现接口
interface Serializable {
serialize(): string;
deserialize(data: string): void;
}
interface Loggable {
log(): void;
}
class UserModel implements Serializable, Loggable {
constructor(public name: string) {}
serialize(): string {
return JSON.stringify(this);
}
deserialize(data: string): void {
const obj = JSON.parse(data);
Object.assign(this, obj);
}
log(): void {
console.log(`User: ${this.name}`);
}
}
this 类型
class Calculator {
constructor(public value: number = 0) {}
add(n: number): this { // 返回 this 实现链式调用
this.value += n;
return this;
}
multiply(n: number): this {
this.value *= n;
return this;
}
getResult(): number {
return this.value;
}
}
class AdvancedCalculator extends Calculator {
pow(n: number): this {
this.value = Math.pow(this.value, n);
return this;
}
}
const calc = new AdvancedCalculator();
calc.add(5).multiply(2).pow(3).getResult(); // 类型正确
模块与命名空间
esModuleInterop
// 不开启 esModuleInterop 时:
import * as React from 'react'; // 命名空间导入
import React = require('react'); // 导入 = require
// 开启 esModuleInterop 时:
import React from 'react'; // 默认导入,更自然
esModuleInterop 在 tsconfig.json 中启用,配合 allowSyntheticDefaultImports 让 CJS 和 ES Module 互操作更顺畅。
declare
// 声明全局类型(通常放在 .d.ts 文件中)
declare global {
interface Window {
__INITIAL_STATE__: Record<string, unknown>;
}
}
// 声明模块(用于无类型 npm 包)
declare module 'some-untyped-lib' {
export function doSomething(): void;
export const VERSION: string;
}
// 声明变量(已存在的全局变量)
declare const $: (selector: string) => any;
.d.ts 声明文件
// types.d.ts —— 只包含类型声明,不包含实现
// 被 tsconfig.json 的 include 或 files 包含
// 声明一个模块的类型
interface UserData {
id: number;
name: string;
}
// 扩展已有类型
declare module '*.css' {
const classes: Record<string, string>;
export default classes;
}
declare module '*.svg' {
const content: React.FC<React.SVGProps<SVGElement>>;
export default content;
}
三斜线指令
// 用于依赖其他声明文件
/// <reference path="./types.d.ts" />
/// <reference types="node" /> // 引入 @types/node
/// <reference lib="es2015" /> // 引入 lib 声明
// 使用场景:
// 1. 手动引用其他 .d.ts 文件(构建工具未自动包含时)
// 2. 声明文件之间的依赖声明
// 3. 在打包前手动关联类型(非模块化场景)
高级类型
类型守卫
// typeof 守卫
function process(value: string | number) {
if (typeof value === 'string') {
return value.toUpperCase(); // 收窄为 string
}
return value.toFixed(2); // 收窄为 number
}
// instanceof 守卫
class Dog { bark() {} }
class Cat { meow() {} }
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
// 自定义类型守卫(is)
interface Fish { swim(): void; }
interface Bird { fly(): void; }
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim(); // 类型收窄为 Fish
} else {
pet.fly();
}
}
// in 守卫
if ('swim' in animal) {
animal.swim(); // 收窄为含 swim 的类型
}
// 可辨别联合
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'rectangle'; width: number; height: number }
| { kind: 'triangle'; base: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'rectangle':
return shape.width * shape.height;
case 'triangle':
return (shape.base * shape.height) / 2;
// default: never —— 穷尽检查
}
}
映射类型(Mapped Types)
// 基础映射类型
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
type Optional<T> = {
[K in keyof T]?: T[K];
};
// 修饰符:+ / - 添加或移除 readonly 和 ?
type Mutable<T> = {
-readonly [K in keyof T]: T[K];
};
type Required<T> = {
[K in keyof T]-?: T[K];
};
// 键名重映射(TypeScript 4.1+)
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
// type GettersUser = { getName: () => string; getAge: () => number; }
// 条件筛选键
type StringKeys<T> = {
[K in keyof T]: T[K] extends string ? K : never;
}[keyof T];
type OnlyStringProps<T> = Pick<T, StringKeys<T>>;
分布式条件类型
// 裸类型参数在条件类型中自动分配
type ToArray<T> = T extends any ? T[] : never;
type Result = ToArray<string | number>; // string[] | number[]
// 实战:剔除指定类型的属性
type ExcludeType<T, U> = {
[K in keyof T]: T[K] extends U ? never : T[K];
}[keyof T];
// 等效于内置的 Extract / Exclude
// Filter 工具
type Filter<T, U> = T extends U ? T : never;
type Numbers = Filter<1 | 'a' | 2 | 'b', number>; // 1 | 2
工程实践
tsconfig.json 关键配置
{
"compilerOptions": {
// ===== 核心 =====
"strict": true,
// 开启以下全部:
// strictNullChecks: true —— null/undefined 不可赋值给其他类型
// strictFunctionTypes: true —— 函数参数逆变检查
// strictBindCallApply: true —— call/apply/bind 参数类型检查
// strictPropertyInitialization: true —— 类属性必须初始化
// noImplicitAny: true —— 禁止隐式 any
// noImplicitThis: true —— 禁止 this 隐式 any
// alwaysStrict: true —— 总是 use strict
// ===== 模块 =====
"module": "ESNext", // ES Module
"moduleResolution": "bundler", // bundler 解析(现代构建工具)
// 或 "node" / "classic"
"target": "ES2022", // 编译目标
"lib": ["ES2022", "DOM"],
// ===== 模块解析 =====
"baseUrl": ".", // 基础路径
"paths": {
"@/*": ["src/*"] // 别名映射
},
"resolveJsonModule": true, // 允许 import json
"allowJs": true, // 允许 JS 文件
"checkJs": false, // 不检查 JS
// ===== 输出 =====
"outDir": "./dist",
"rootDir": "./src",
"declaration": true, // 生成 .d.ts 声明文件
"declarationMap": true, // 声明文件 sourcemap
"sourceMap": true,
// ===== 互操作 =====
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"isolatedModules": true, // 确保每个文件可独立编译(对 Babel/esbuild 重要)
// ===== 质量 =====
"skipLibCheck": true, // 跳过 .d.ts 类型检查(加速编译)
"forceConsistentCasingInFileNames": true,
"noUnusedLocals": true, // 未使用局部变量报错
"noUnusedParameters": true, // 未使用参数报错
"noFallthroughCasesInSwitch": true // switch 穿透报错
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
类型声明文件管理
主流方式:
- 自带类型:库自带
.d.ts文件(package.json的types字段指向) - @types/:社区维护的类型包(
npm i -D @types/react) - 自定义声明:项目内的
.d.ts文件
项目内声明文件组织:
// src/types/global.d.ts —— 全局类型扩展
export {};
declare global {
interface Window {
__APP_CONFIG__: { apiUrl: string; env: string };
}
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production';
API_KEY: string;
}
}
}
// src/types/modules.d.ts —— 第三方模块类型声明
declare module 'untyped-lib' {
export function run(config: Record<string, unknown>): Promise<void>;
export const version: string;
}
// src/types/images.d.ts —— 静态资源类型
declare module '*.svg' {
import type { FC, SVGProps } from 'react';
const content: FC<SVGProps<SVGSVGElement>>;
export default content;
}
declare module '*.module.css' {
const classes: Record<string, string>;
export default classes;
}
类型兼容性与逆变/协变
// 协变(Covariance)—— 保持父子关系
// 只读属性:Dog extends Animal → Readonly<Dog> extends Readonly<Animal>
interface Animal { name: string; }
interface Dog extends Animal { bark(): void; }
let animals: Animal[] = [];
let dogs: Dog[] = [];
animals = dogs; // OK(数组是协变的)
// 逆变(Contravariance)—— 反转父子关系
// 函数参数:Dog extends Animal → (Animal) => void extends (Dog) => void
type FnDog = (dog: Dog) => void;
type FnAnimal = (animal: Animal) => void;
let fDog: FnDog = (d) => d.bark();
let fAnimal: FnAnimal = (a) => console.log(a.name);
fDog = fAnimal; // OK(严格模式下:函数参数逆变)
// 安全原因:fDog 期望传入 Dog,fAnimal 接受 Animal(更宽泛),传入 Dog 没问题
strictFunctionTypes 开启后:函数参数逆变(更安全)。
条件类型实战
// 1. 深度提取 API 返回类型
type ApiResponse<T> = { code: number; data: T; message: string };
type ExtractData<T> = T extends ApiResponse<infer D> ? D : never;
type UserResponse = ApiResponse<{ id: number; name: string }>;
type UserData = ExtractData<UserResponse>; // { id: number; name: string }
// 2. 函数参数中的第一个参数类型
type FirstArg<T> = T extends (a: infer A, ...rest: any[]) => any ? A : never;
// 3. 联合类型转换为交叉类型(Union to Intersection)
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends
(k: infer I) => void ? I : never;
type T = UnionToIntersection<{ a: 1 } | { b: 2 }>;
// { a: 1 } & { b: 2 }