# 进阶之路：类型工具与声明文件

## 概述

前几篇我们学会了 TypeScript 的日常用法：基础类型、接口、函数、类、泛型、模块。

但 TypeScript 的类型系统远比这强大——**它实际上是一门独立的编程语言**。你可以对类型做运算、条件判断、循环遍历、字符串拼接……

本篇的内容偏"进阶"。理解它们需要一段时间，但一旦掌握，你就能写出更灵活、更安全的类型定义。

## 映射类型

映射类型可以基于一个类型，生成另一个类型。它的语法像循环：

```typescript
type T = { [K in "a" | "b" | "c"]: boolean }
// 等价于 { a: boolean; b: boolean; c: boolean }
```

真实场景——把一个接口的所有属性变为可选：

```typescript
interface Person {
  name: string
  age: number
  email: string
}

// 手动写一份可选版本
type PartialPerson = {
  name?: string
  age?: number
  email?: string
}
```

用映射类型自动生成：

```typescript
type MyPartial<T> = {
  [K in keyof T]?: T[K]
}

type PartialPerson = MyPartial<Person>
// { name?: string; age?: number; email?: string }
```

在映射类型中可以加修饰符：`?` 表示可选，`readonly` 表示只读。前缀 `-` 表示去除某个修饰符：

```typescript
// 去除所有 readonly
type Mutable<T> = {
  -readonly [K in keyof T]: T[K]
}

// 去除所有可选标记
type Required<T> = {
  [K in keyof T]-?: T[K]
}
```

## 条件类型

条件类型为类型添加了"if-else"逻辑：

```typescript
type IsString<T> = T extends string ? "是字符串" : "不是字符串"

type A = IsString<"hello">   // "是字符串"
type B = IsString<42>        // "不是字符串"
```

> [!quote] 理解
> 条件类型类似于三元运算符：`T extends U ? X : Y`。如果 T 能赋值给 U，结果为 X，否则为 Y。

实际用途——根据参数类型决定返回值类型：

```typescript
type ApiResponse<T> = T extends "user" ? { id: number; name: string }
  : T extends "post" ? { id: number; title: string }
  : { error: string }

type UserRes = ApiResponse<"user">  // { id: number; name: string }
type PostRes = ApiResponse<"post">  // { id: number; title: string }
```

### 条件类型中的 infer

`infer` 可以在条件类型中声明一个"待推断"的类型变量：

```typescript
// 获取数组元素的类型
type ElementType<T> = T extends (infer E)[] ? E : never

type A = ElementType<string[]>  // string
type B = ElementType<number[]>  // number
type C = ElementType<boolean>   // never（不是数组）

// 获取函数返回值的类型
type ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never

type Fn = (x: number) => string
type R = ReturnType<Fn>  // string
```

`infer` 是高级类型编程中最强大的工具之一。它让 TypeScript 帮我们"反向推导"出类型。

## 模板字面量类型

TypeScript 4.1 引入了模板字面量类型——就像 JavaScript 的模板字符串，但在类型层面：

```typescript
type EventName = `on${Capitalize<string>}`

// 组合字符串字面量
type Size = "small" | "medium" | "large"
type Color = "red" | "green" | "blue"

type ButtonVariant = `${Size}-${Color}`
// "small-red" | "small-green" | "small-blue"
// | "medium-red" | "medium-green" | "medium-blue"
// | "large-red" | "large-green" | "large-blue"
```

实用的例子——约束事件处理函数的命名：

```typescript
type EventHandler<K extends string> = `on${Capitalize<K>}`

type ClickHandler = EventHandler<"click">  // "onClick"
type ChangeHandler = EventHandler<"change"> // "onChange"
```

## 内置工具类型

TypeScript 内置了一套常用的泛型工具类型，可以让你的类型编程事半功倍：

### 对象操作

```typescript
interface User {
  name: string
  age: number
  email: string
  password: string
}

// Partial<T> —— 所有属性变为可选
type PartialUser = Partial<User>
// { name?: string; age?: number; email?: string; password?: string }

// Required<T> —— 所有属性变为必选
type RequiredUser = Required<PartialUser>
// { name: string; age: number; email: string; password: string }

// Readonly<T> —— 所有属性变为只读
type ReadonlyUser = Readonly<User>

// Pick<T, K> —— 挑选部分属性
type PublicUser = Pick<User, "name" | "email">
// { name: string; email: string }

// Omit<T, K> —— 排除部分属性
type InternalUser = Omit<User, "password">
// { name: string; age: number; email: string }
```

### 联合类型操作

```typescript
type Status = "idle" | "loading" | "success" | "error"

// Exclude<T, U> —— 排除某些类型
type NoLoading = Exclude<Status, "loading">
// "idle" | "success" | "error"

// Extract<T, U> —— 提取某些类型
type LoadingStates = Extract<Status, "loading" | "error">
// "loading" | "error"

// NonNullable<T> —— 去除 null 和 undefined
type Maybe = string | null | undefined
type Sure = NonNullable<Maybe>  // string
```

### 记录类型

```typescript
// Record<K, V> —— 构造键值对类型
type PageRoutes = Record<string, string>
const routes: PageRoutes = {
  home: "/",
  about: "/about",
}

// 更精确的用法——限制键
type PageName = "home" | "about" | "contact"
type PageInfo = Record<PageName, { title: string; path: string }>

const pages: PageInfo = {
  home: { title: "首页", path: "/" },
  about: { title: "关于", path: "/about" },
  contact: { title: "联系", path: "/contact" },
}
```

### 其他常用工具

```typescript
// Awaited<T> —— 获取 Promise 内部类型
type AsyncResult = Awaited<Promise<string>>  // string
type DeepAsync = Awaited<Promise<Promise<number>>>  // number

// Parameters<T> —— 获取函数参数类型（元组）
type Fn = (name: string, age: number) => void
type FnParams = Parameters<Fn>  // [string, number]

// ConstructorParameters<T> —— 获取构造参数类型
class Person { constructor(name: string, age: number) {} }
type PersonParams = ConstructorParameters<typeof Person>  // [string, number]

// ReturnType<T> —— 获取函数返回值类型
type FnReturn = ReturnType<() => string>  // string

// InstanceType<T> —— 获取类实例的类型
type PersonInstance = InstanceType<typeof Person>  // Person
```

## 声明文件（.d.ts）

当你在 TypeScript 中使用 JavaScript 库（如 lodash、jQuery）时，需要类型声明文件来告诉 TypeScript 这些函数的类型。

```typescript
// lodash 的简化声明
declare function chunk<T>(array: T[], size?: number): T[][]
declare function shuffle<T>(array: T[]): T[]
```

### 声明文件的来源

- **自带声明**：库的 `package.json` 中有 `"types": "index.d.ts"`，如 Vue
- **DefinitelyTyped**：`npm install @types/lodash`，社区维护的声明
- **自己写**：在项目中创建 `.d.ts` 文件

### declare 关键字

`declare` 告诉 TypeScript"这个变量存在，但实现在别处"：

```typescript
// 声明全局变量
declare const API_VERSION: string

// 声明全局函数
declare function fetchData(url: string): Promise<unknown>

// 声明模块
declare module "my-lib" {
  export function doSomething(): void
  export const VERSION: string
}

// 声明类
declare class MyClass {
  constructor(name: string)
  greet(): string
}
```

### 环境配置

在 `.d.ts` 文件中声明全局类型，整个项目都可以使用：

```typescript
// env.d.ts —— 放在项目 src 目录下
declare namespace NodeJS {
  interface ProcessEnv {
    NODE_ENV: "development" | "production" | "test"
    API_URL: string
  }
}

// 使用
const env = process.env.NODE_ENV  // 类型为 "development" | "production" | "test"
```

> [!warning] 注意
> 声明文件（`.d.ts`）只包含类型信息，不包含实现。它就像一份"说明书"，告诉 TypeScript 外部世界的类型。编译时不会生成对应的 JS 代码。

---

进阶之路到这里就告一段落了。你接触了 TypeScript 类型系统的"编程"能力——映射、条件、推断、模板字面量、工具类型。

**好的，理论足够多了。下一篇是真正的收尾——用 TypeScript 从头到尾做一个完整项目。**


---

> 作者: Aphros  
> URL: https://blog.papergate.top/posts/11.%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF%E7%B1%BB%E5%9E%8B%E5%B7%A5%E5%85%B7%E4%B8%8E%E5%A3%B0%E6%98%8E%E6%96%87%E4%BB%B6/  

