共计 14537 个字符,预计需要花费 37 分钟才能阅读完成。
1. 高级类型
条件类型
// 基础条件类型
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// 嵌套条件类型
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" :
"object";
type T0 = TypeName<string>; // "string"
type T1 = TypeName<"hello">; // "string"
type T2 = TypeName<true>; // "boolean"
type T3 = TypeName<() => void>; // "function"
type T4 = TypeName<string[]>; // "object"
// 分布式条件类型
type ToArray<T> = T extends any ? T[] : never;
type StrArrOrNumArr = ToArray<string | number>; // string[] | number[]
// 过滤类型
type Filter<T, U> = T extends U ? T : never;
type StringOrNumber = Filter<string | number | boolean, string | number>; // string | number
// 排除 null 和 undefined
type NonNullable<T> = T extends null | undefined ? never : T;
type T5 = NonNullable<string | number | undefined>; // string | number
type T6 = NonNullable<string[] | null>; // string[]
推断类型
// 使用 infer 推断类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type T7 = ReturnType<() => string>; // string
type T8 = ReturnType<(s: string) => void>; // void
type T9 = ReturnType<<T>() => T>; // unknown
type T10 = ReturnType<typeof Math.random>; // number
// 推断函数参数
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
type T11 = Parameters<() => void>; // []
type T12 = Parameters<(s: string) => void>; // [s: string]
type T13 = Parameters<<T>(arg: T) => T>; // [arg: unknown]
// 推断构造函数参数
type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;
class Person {constructor(public name: string, public age: number) {}}
type T14 = ConstructorParameters<typeof Person>; // [name: string, age: number]
// 推断实例类型
type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : any;
type T15 = InstanceType<typeof Person>; // Person
映射类型修饰符
// 移除只读修饰符
type CreateMutable<T> = {-readonly [P in keyof T]: T[P];
};
interface LockedAccount {
readonly id: string;
readonly name: string;
}
type UnlockedAccount = CreateMutable<LockedAccount>;
// {id: string; name: string;}
// 移除可选修饰符
type Concrete<T> = {[P in keyof T]-?: T[P];
};
interface MaybeUser {
id: number;
name?: string;
age?: number;
}
type User = Concrete<MaybeUser>;
// {id: number; name: string; age: number;}
// 同时移除只读和可选
type MutableRequired<T> = {-readonly [P in keyof T]-?: T[P];
};
键重映射
// 使用 as 子句重映射键
type Getters<T> = {[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
interface Person {
name: string;
age: number;
location: string;
}
type LazyPerson = Getters<Person>;
// {// getName: () => string;
// getAge: () => number;
// getLocation: () => string;
// }
// 过滤键
type RemoveKindField<T> = {[P in keyof T as Exclude<P, "kind">]: T[P];
};
interface Circle {
kind: "circle";
radius: number;
}
type KindlessCircle = RemoveKindField<Circle>;
// {radius: number}
// 根据值类型过滤键
type ExtractPropertyNames<T, U> = {[K in keyof T]: T[K] extends U ? K : never;
}[keyof T];
interface Example {
name: string;
age: number;
isActive: boolean;
tags: string[];
}
type StringKeys = ExtractPropertyNames<Example, string>; // "name" | "tags"
2. 模板字面量类型
基础模板字面量
type World = "world";
type Greeting = `hello ${World}`; // "hello world"
type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
// "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"
type Lang = "en" | "ja" | "pt";
type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}`;
// "en_welcome_email_id" | "en_email_heading_id" | ... 12 more ...
字符串操作类型
// 内置字符串操作类型
type UppercaseGreeting = Uppercase<"hello world">; // "HELLO WORLD"
type LowercaseGreeting = Lowercase<"HELLO WORLD">; // "hello world"
type CapitalizedGreeting = Capitalize<"hello world">; // "Hello world"
type UncapitalizedGreeting = Uncapitalize<"Hello World">; // "hello World"
// 创建访问器方法
type Getter<T extends string> = `get${Capitalize<T>}`;
type Setter<T extends string> = `set${Capitalize<T>}`;
type Name = "name";
type NameAccessors = Getter<Name> | Setter<Name>; // "getName" | "setName"
// 事件处理函数
type EventName = "click" | "hover" | "focus";
type EventHandlerName = `on${Capitalize<EventName>}`;
// "onClick" | "onHover" | "onFocus"
模板字面量推断
// 解析路径参数
type Route = "/user/:id" | "/post/:slug" | "/category/:name";
type ExtractRouteParams<T extends string> =
T extends `${string}:${infer Param}/${infer Rest}`
? Param | ExtractRouteParams<`/${Rest}`>
: T extends `${string}:${infer Param}`
? Param
: never;
type Params = ExtractRouteParams<Route>; // "id" | "slug" | "name"
// 更复杂的路径解析
type ParseRoute<T extends string> =
T extends `${infer Start}/:${infer Param}/${infer Rest}`
? {[K in Param | keyof ParseRoute<`/${Rest}`>]: string }
: T extends `${infer Start}/:${infer Param}`
? {[K in Param]: string }
: {};
type UserRoute = ParseRoute<"/user/:id">; // {id: string}
type PostRoute = ParseRoute<"/post/:slug/comments/:commentId">; // {slug: string; commentId: string}
3. 声明合并
接口合并
// 相同名称的接口会自动合并
interface Box {
height: number;
width: number;
}
interface Box {
scale: number;
// height: string; // 错误:后续属性声明必须属于同一类型
}
let box: Box = {height: 5, width: 6, scale: 10};
// 合并函数重载
interface Cloner {clone(animal: Animal): Animal;
}
interface Cloner {clone(animal: Sheep): Sheep;
}
interface Cloner {clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
}
// 等价于:interface Cloner {clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
clone(animal: Sheep): Sheep;
clone(animal: Animal): Animal;
}
// 命名空间与类合并
class Album {label: Album.AlbumLabel = new Album.AlbumLabel();
}
namespace Album {
export class AlbumLabel {name: string = "Default Label";}
export const version = "1.0";
}
const album = new Album();
console.log(album.label.name); // "Default Label"
console.log(Album.version); // "1.0"
命名空间合并
namespace Animals {export class Zebra {}
}
namespace Animals {
export interface Legged {numberOfLegs: number;}
export class Dog {}}
// 等价于:namespace Animals {
export interface Legged {numberOfLegs: number;}
export class Zebra {}
export class Dog {}}
枚举合并
enum Color {
Red = 1,
Green = 2
}
enum Color {Blue = 4}
// 等价于:enum Color {
Red = 1,
Green = 2,
Blue = 4
}
console.log(Color.Red); // 1
console.log(Color.Blue); // 4
4. 类型守卫与类型断言
自定义类型守卫
// 使用类型谓词
function isString(value: unknown): value is string {return typeof value === 'string';}
function isNumber(value: unknown): value is number {return typeof value === 'number';}
function isDate(value: unknown): value is Date {return value instanceof Date;}
// 使用 in 操作符
interface Admin {
name: string;
privileges: string[];
}
interface Employee {
name: string;
startDate: Date;
}
type UnknownEmployee = Employee | Admin;
function printEmployeeInformation(emp: UnknownEmployee) {console.log("Name:" + emp.name);
if ("privileges" in emp) {console.log("Privileges:" + emp.privileges);
}
if ("startDate" in emp) {console.log("Start Date:" + emp.startDate);
}
}
// 使用字面量类型守卫
type NetworkLoadingState = {state: "loading";};
type NetworkFailedState = {
state: "failed";
code: number;
};
type NetworkSuccessState = {
state: "success";
response: {
title: string;
duration: number;
summary: string;
};
};
type NetworkState =
| NetworkLoadingState
| NetworkFailedState
| NetworkSuccessState;
function logger(state: NetworkState): string {switch (state.state) {
case "loading":
return "Downloading...";
case "failed":
return `Error ${state.code} downloading`;
case "success":
return `Downloaded ${state.response.title} - ${state.response.summary}`;
}
}
断言函数
// 断言函数
function assert(condition: any, msg?: string): asserts condition {if (!condition) {throw new Error(msg);
}
}
function assertIsString(val: any): asserts val is string {if (typeof val !== "string") {throw new Error("Not a string!");
}
}
function yell(str: any) {assertIsString(str);
return str.toUpperCase(); // str 现在是 string 类型}
// 使用断言函数处理未知数据
function processUserData(data: unknown) {assert(typeof data === "object" && data !== null, "Data must be an object");
assert("name" in data && typeof data.name === "string", "Name must be a string");
assert("age" in data && typeof data.age === "number", "Age must be a number");
// 现在 TypeScript 知道 data 的形状
console.log(`Name: ${data.name}, Age: ${data.age}`);
}
5. 异步编程
Promise 类型
// Promise 基础类型
const promise: Promise<string> = new Promise((resolve, reject) => {setTimeout(() => {resolve("Hello, World!");
}, 1000);
});
// 异步函数返回类型
async function fetchData(): Promise<string> {const response = await fetch('/api/data');
const data = await response.text();
return data;
}
// 处理多个 Promise
async function fetchMultipleData(): Promise<[string, number]> {const [data1, data2] = await Promise.all([
fetch('/api/data1').then(r => r.text()),
fetch('/api/data2').then(r => r.json())
]);
return [data1, data2];
}
// 错误处理
async function safeFetch<T>(url: string): Promise<T | null> {
try {const response = await fetch(url);
if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();} catch (error) {console.error('Fetch error:', error);
return null;
}
}
高级异步模式
// 带重试的异步函数
async function fetchWithRetry<T>(
url: string,
maxRetries: number = 3,
delay: number = 1000
): Promise<T> {for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {const response = await fetch(url);
if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();} catch (error) {if (attempt === maxRetries) {throw error;}
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('All retry attempts failed');
}
// 异步迭代器
async function* asyncGenerator(): AsyncGenerator<number, void, unknown> {
let i = 0;
while (i < 3) {await new Promise(resolve => setTimeout(resolve, 1000));
yield i++;
}
}
async function consumeAsyncGenerator() {for await (const value of asyncGenerator()) {console.log(value);
}
}
// Promise 工具类型
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
async function example() {const result = await Promise.resolve(Promise.resolve(42));
type ResultType = Awaited<typeof result>; // number
}
6. 性能优化与最佳实践
类型性能优化
// 1. 使用接口而不是类型别名(对于对象类型)// 推荐
interface User {
id: number;
name: string;
email: string;
}
// 不推荐(对于对象类型)type UserType = {
id: number;
name: string;
email: string;
};
// 2. 使用 const 枚举
const enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT"
}
// 3. 避免深度嵌套的类型
// 不推荐
type DeepNested = {
level1: {
level2: {
level3: {
level4: {value: string;};
};
};
};
};
// 推荐
interface Level4 {value: string;}
interface Level3 {level4: Level4;}
interface Level2 {level3: Level3;}
interface Level1 {level2: Level2;}
// 4. 使用索引签名谨慎
interface StringMap {[key: string]: string;
}
// 更好的做法是使用 Record 工具类型
type BetterStringMap = Record<string, string>;
工程化最佳实践
// 1. 使用 barrel exports
// src/components/index.ts
export {Button} from './Button';
export {Input} from './Input';
export {Modal} from './Modal';
// 2. 配置路径映射
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./",
"paths": {"@/*": ["src/*"],
"@/components/*": ["src/components/*"],
"@/utils/*": ["src/utils/*"]
}
}
}
// 使用
import {Button} from '@/components';
import {formatDate} from '@/utils/date';
// 3. 环境变量类型安全
interface ProcessEnv {
readonly NODE_ENV: 'development' | 'production' | 'test';
readonly API_URL: string;
readonly APP_VERSION: string;
}
declare global {
namespace NodeJS {interface ProcessEnv extends ProcessEnv {}
}
}
// 4. 错误处理模式
class AppError extends Error {
constructor(
message: string,
public code: string,
public statusCode: number = 500
) {super(message);
this.name = 'AppError';
}
}
function handleError(error: unknown): never {if (error instanceof AppError) {console.error(`AppError [${error.code}]: ${error.message}`);
throw error;
}
if (error instanceof Error) {console.error(`Unexpected error: ${error.message}`);
throw new AppError('Internal server error', 'INTERNAL_ERROR');
}
console.error('Unknown error occurred');
throw new AppError('Unknown error', 'UNKNOWN_ERROR');
}
7. 测试与调试
单元测试配置
// jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: [
'**/__tests__/**/*.+(ts|tsx|js)',
'**/?(*.)+(spec|test).+(ts|tsx|js)'
],
transform: {'^.+\\.(ts|tsx)$': 'ts-jest',
},
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
],
};
// src/utils/math.test.ts
import {add, multiply, divide} from './math';
describe('Math utilities', () => {describe('add', () => {it('should add two numbers correctly', () => {expect(add(1, 2)).toBe(3);
expect(add(-1, 1)).toBe(0);
expect(add(0, 0)).toBe(0);
});
it('should handle decimal numbers', () => {expect(add(1.5, 2.5)).toBe(4);
expect(add(0.1, 0.2)).toBeCloseTo(0.3);
});
});
describe('multiply', () => {it('should multiply two numbers correctly', () => {expect(multiply(2, 3)).toBe(6);
expect(multiply(-2, 3)).toBe(-6);
expect(multiply(0, 5)).toBe(0);
});
});
describe('divide', () => {it('should divide two numbers correctly', () => {expect(divide(6, 2)).toBe(3);
expect(divide(10, 4)).toBe(2.5);
});
it('should throw error when dividing by zero', () => {expect(() => divide(5, 0)).toThrow('Division by zero');
});
});
});
// src/utils/math.ts
export function add(a: number, b: number): number {return a + b;}
export function multiply(a: number, b: number): number {return a * b;}
export function divide(a: number, b: number): number {if (b === 0) {throw new Error('Division by zero');
}
return a / b;
}
调试配置
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug TypeScript",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/dist/index.js",
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"sourceMaps": true,
"preLaunchTask": "tsc: build",
"console": "integratedTerminal"
},
{
"name": "Debug Jest Tests",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": ["--runInBand", "--no-cache"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"windows": {"program": "${workspaceFolder}/node_modules/jest/bin/jest"
}
},
{
"name": "Debug Current Test File",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": [
"${relativeFile}",
"--no-cache"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"windows": {"program": "${workspaceFolder}/node_modules/jest/bin/jest"
}
}
]
}
8. 构建与部署
现代化构建配置
// package.json
{
"name": "typescript-project",
"version": "1.0.0",
"scripts": {
"build": "tsc",
"build:watch": "tsc --watch",
"build:production": "tsc --project tsconfig.prod.json",
"dev": "ts-node src/index.ts",
"start": "node dist/index.js",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"lint": "eslint src/**/*.ts",
"lint:fix": "eslint src/**/*.ts --fix",
"type-check": "tsc --noEmit"
},
"devDependencies": {
"@types/jest": "^29.0.0",
"@types/node": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.0",
"jest": "^29.0.0",
"ts-jest": "^29.0.0",
"ts-node": "^10.0.0",
"typescript": "^4.9.0"
}
}
// tsconfig.prod.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": false,
"declaration": false,
"removeComments": true,
"noEmitOnError": true
},
"exclude": ["**/*.test.ts", "**/*.spec.ts", "node_modules"]
}
Docker 部署配置
# Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
COPY tsconfig.json ./
RUN npm ci
COPY src/ ./src/
RUN npm run build
FROM node:18-alpine AS runtime
WORKDIR /app
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder --chown=nextjs:nodejs /app/dist/ ./dist/
USER nextjs
EXPOSE 3000
CMD ["node", "dist/index.js"]
持续集成配置
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{matrix.node-version}}
uses: actions/setup-node@v3
with:
node-version: ${{matrix.node-version}}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Type check
run: npm run type-check
- name: Lint
run: npm run lint
- name: Test
run: npm test
- name: Build
run: npm run build
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: user/app:latest
正文完

