Skip to content

TypeScript 类型实战专题

类型体操

要点速览

shell
# 何为类型体操?
它是一种思维和能力的训练:就像运动员练体操是为了锻炼核心力量、平衡感和身体控制能力。程序员玩类型体操,也是在锻炼自己的抽象思维、逻辑推理和对 TypeScript 类型系统的深层理解。这个过程能让你真正搞懂 extends、infer、协变、逆变等概念,从而在日常开发中写出更精准、更健壮的类型定义。

# 目的
通过复杂的类型运算获得最终我们需要的类型,

# 核心
遍历、判断、递归、推断,用类型编程代替运行时逻辑。

# 总结
typeof 取值,keyof 取键,in 遍历生成对象,extends 判断从属,infer 提取拆包,as 改键,never 删键,
Partial 可选,Pick 选取,Exclude 排除,ReturnType 提取返回值。

6 个关键词

js
// 类型体操 6 大原生关键字(灵魂 6 件套)
1. typeof把值变成类型
2. keyof把对象变成键联合
3. in遍历键联合生成映射对象
4. extends做类型条件判断分布式判断
5. infer extends 里拆包提取内部类型
6. as + never过滤删除对象属性

// 1. typeof 提取值的类型
const obj = { a: 1 }
type T = typeof obj // {a:number}

// 2. keyof 从对象类型中提取所有键联合
type T = {a:string;b:number}
type K = keyof T // 'a'|'b'

// 3. in 类型的 for 循环,遍历键生成对象
type MyRecord<K extends string> = { [P in K]: string }

// 4. extends 类型从属判断 / 条件类型 / 泛型约束,判断 A 是不是 B 的子类,能否赋值给 B
type IsStr<T> = T extends string ? true : false

// 5. infer 推断,提取内部子类型,在 extends 条件里,提取、解构、拆包嵌套类型
type Return<T> = T extends (...args:any[])=>infer R ? R : never

// 6. 映射重映射 as 修改键名、过滤删除键(返回 never = 删除),as 改键,never 删键
type Filter<T> = { [K in keyof T as K extends 'a'?never:K]: T[K] }

TS 内置全部类型体操工具 API 原理

全部底层原理 = keyof + in + extends + infer

js
/****** 一、映射类型类(基于 in) ******/
// 1. Record<K, T> 
作用定义键为 K值为 T 的对象
底层{ [P in K]: T }
// 2. Partial<T>
作用 T 所有属性变成可选
底层{ [K in keyof T]?: T[K] }
// 3. Required<T>
作用把所有属性变成必填
底层{ [K in keyof T]-?: T[K] }
// 4. Readonly<T>
作用所有属性变成只读
底层{ readonly [K in keyof T]: T[K] }

/****** 二、选取/过滤类型(keyof + extends) ******/
// 1. Pick<T, K>
作用 T 中挑选K 几个属性
底层{ [P in K]: T[P] }
// 2. Omit<T, K>
作用 T 中剔除K 几个属性
底层{ [P in keyof T as P extends K?never:P]: T[P] }

/****** 三、提取 / 排除类型(extends 分布式) ******/
// 1. Exclude<T, U>
作用 T 中排除可赋值给 U 的类型
底层T extends U ? never : T
// 2. Extract<T, U>
作用 T 中提取可赋值给 U 的类型
底层T extends U ? T : never

/****** 四、infer 拆包提取类(进阶难点) ******/
// 1. ReturnType<T>
作用提取函数返回值类型
底层T extends (...args:any[])=>infer R ? R : never
// 2. Parameters<T>
作用提取函数参数元组类型
底层T extends (...args:infer P)=>any ? P : never
// 3. Awaited<T>
作用提取Promise 包裹的内部类型
底层递归拆 Promise type MyAwaited<T> = T extends Promise<infer R> ? MyAwaited<R> : T
// 4. ConstructorParameters / InstanceType
作用提取构造函数参数实例类型

/****** 五、字符串类型体操 API(进阶) ******/
Uppercase / Lowercase / Capitalize / Uncapitalize 大小写首字母转换

案例

js
// 1. 辅助类型:生成一个长度为 N 的数组
type BuildArray<N extends number, T extends any[] = []> = 
  T['length'] extends N ? T : BuildArray<N, [...T, any]>;

// 2. 加法:把两个数组拼起来,然后取长度
type Add<A extends number, B extends number> = 
  [...BuildArray<A>, ...BuildArray<B>]['length'];

// 测试
type Result = Add<3, 5>; // type Result = 8 ✅

TS 结合 Vue3

要点速览

shell
# 基础原则
1. 全站 <script setup lang="ts">,抛弃选项式 API
2. 组件 Props / Emits 必须纯类型写法,不运行时冗余校验
3. 复杂数据优先接口 / 类型,拒绝 any
4. 全局变量、组件、路由、环境变量全部补全局类型声明

基础类型自行推断,复杂类型定义接口/类型
方法使用泛型

Props 类型

js
// 1. defineProps 类型,纯 TS 类型定义(推荐,最简),带类型 + 必选校验
const props = defineProps<{
  id: number
  name: string // 必传
  list?: string[] // 可选
}>()

// 2. 需要默认值 + TS 结合
interface Props {
  page?: number
  size?: number
}

const props = withDefaults(defineProps<Props>(), {
  page: 1,
  size: 10
})

// 注意
必选 / 可选严格区分

Emits 类型

js
// 1. defineEmits 类型,纯 TS 类型定义(推荐,最简)
const emit = defineEmits<{
  (e: 'change', val: string): void
  (e: 'delete', id: number): void
}>()

// 使用
emit('change', 'test')

响应式数据/计算属性/插槽 类型

js
/****** 一、ref 基础类型 ******/
// 1. 自动推导为 Ref<string>
const name = ref('')
// 2. 手动限制类型
const count = ref<number>(0)

/****** 二、reactive 复杂对象类型 ******/
interface User {
  id: number
  nickname: string
}

const user = reactive<User>({
  id: 0,
  nickname: ''
})

/****** 三、浅层响应式 shallowRef、shallowReactive ******/
const list = shallowRef<Item[]>([])
const info = shallowReactive<Info>({...})

/****** 四、解构 toRefs :自行推到 ******/
const { id, name } = toRefs(user)

/****** 五、计算属性 computed:自动推导,复杂场景手动约束返回值 ******/
const total = computed<number>(() => {
  return list.value.length
})

/****** 六、插槽 defineSlots ******/
// 具名插槽 + 作用域插槽类型
defineSlots<{
  default?: () => any
  header?: (props: { title: string }) => any
}>()

Pinia + TS 规范

js
// 1. 只需 定义 State 接口
// 2. defineStore 自动推断
// 3. getters /actions 天然类型约束

interface State {
  token: string
  userInfo: User | null
}

export const useUserStore = defineStore('user', {
  state: (): State => ({
    token: '',
    userInfo: null
  })
})

全局组件 / 全局属性 类型补全

js
/****** 一、全局注册组件,TS 识别 ******/
// 在 src/typings/components.d.ts 中
declare module 'vue' {
  export interface GlobalComponents {
    CustomBtn: typeof import('@/components/CustomBtn.vue')['default']
  }
}
export {}

/****** 二、扩展全局自定义属性 ******/
// 在 src/typings/properties.d.ts 中
declare module 'vue' {
  interface ComponentCustomProperties {
    $bus: EventEmitter
  }
}

路由 Route 完整类型

js
// 在 src/typings/router.d.ts 中
// 1. 路由项单独声明类型
// 2. useRoute 元信息、params、query 约束
declare module 'vue-router' {
  interface RouteMeta {
    title?: string
    requiresAuth?: boolean
  }
}

环境变量 .env 类型

js
// 在 src/typings/env.d.ts 中
interface ImportMetaEnv {
  readonly VITE_BASE_URL: string
  readonly VITE_ENV: 'dev' | 'prod'
}

interface ImportMeta {
  readonly env: ImportMetaEnv
}

API 请求统一泛型封装

js
export interface ResData<T> {
  code: number
  data: T
  msg: string
}

function request<T>(url: string): Promise<ResData<T>> {
  // ...
}

// 使用
request<User[]>('/api/list')

shell
 不要 defineProps({ xxx: Object }) 运行时写法
 不要滥用 any,未知结构用 unknown + 类型守卫
 不要直接解构 reactive,用 toRefs
 全局统一 .d.ts 管理类型,复用 interface
 大型项目抽离 types/ 目录,统一管理业务类型

TS 结合 React

要点速览

shell
# 基础原则
1. 能自动推断绝不手动写类型
2. Props 只定义业务字段,原生属性直接继承
3. 拒绝 any,用 unknown + 类型守卫
4. 类型复用 > 重复定义

统一用 type,不用 interface
Props 命名:组件名 + Props ButtonProps
事件处理函数:handleXxx
不写 React.FC(过时写法)
Props 解构直接写在函数参数
能自动推断的类型绝不写
方法使用泛型

基础类型自行推断,复杂类型定义接口/类型
能推断 => 不写
能继承 => 不写
能复用 => 不重复
能自动生成类型 => 不手写

Props类型/组件返回值/ref类型

js
// 定义传入的 props 类型
type CardProps = {
  title: string;
  desc?: string; 
  children: React.ReactNode;
  onClick?: () => void;
};

export function Card({ title, desc, children, onClick }: CardProps) {
  return (
    <div onClick={onClick}>
      <h2>{title}</h2>
      {desc && <p>{desc}</p>}
      {children}
    </div>
  );
}

// props 带默认值
type BadgeProps = {
  text: string;
  color?: "red" | "blue";
};
export function Badge({ text, color = "red" }: BadgeProps) {
  return <span className={`badge-${color}`}>{text}</span>;
}

// 定义函数的返回值类型,返回原生 HTML 元素
import type { ButtonHTMLAttributes } from "react";
type ButtonProps = {
  variant?: "primary" | "secondary";
} & ButtonHTMLAttributes<HTMLButtonElement>;
export function Button({ variant = "primary", ...props }: ButtonProps) {
  return <button {...props} className={`btn ${variant}`} />;
}

// React 19 直接用 ref(无需 forwardRef)
type InputProps = {
  placeholder?: string;
  ref: React.Ref<HTMLInputElement>;
};
export function MyInput({ placeholder, ref }: InputProps) {
  return <input ref={ref} placeholder={placeholder} />;
}

hooks类型/事件类型

js
/********* 一、useState(自动推断优先)*********/
// ✅ 自动推断:number
const [count, setCount] = useState(0);
// ✅ 联合类型(明确状态)
const [status, setStatus] = useState<"idle" | "loading" | "done">("idle");

/********* 二、自定义 Hooks(泛型 + 安全)*********/
function useModal() {
  const [visible, setVisible] = useState(false);
  const open = () => setVisible(true);
  const close = () => setVisible(false);
  return { visible, open, close } as const;
}

/********* 三、事件类型(不用记,直接复制)*********/
// 点击
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {};
// 输入
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {};
// 表单提交
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {};

React 19 专属 TS 优雅写法

js
// useActionState(表单)
type FormState = {
  success: boolean;
  message?: string;
};
async function myAction(prev: FormState, formData: FormData): Promise<FormState> {
  return { success: true };
}
function Form() {
  const [state, action, pending] = useActionState(myAction, { success: false });
  return <form action={action}>{/* ... */}</form>;
}

// use (Promise) 异步获取
const user = use(fetchUser(id));

TS 与 AI

2026 年新项目必须用 TS,本质是:AI 代码生成、重构、协作、大型项目维护,全都强依赖类型信息; 没有 TS,AI 写的代码质量低、重构不敢动、团队协作沟通成本高、长期维护就是灾难。

要点速览

拒绝 TS = 放弃 AI 提效、放任代码腐化、拉高线上 bug 风险。

shell
2026 年,TS 不是可选项,而是必选项。AI 代码生成要类型、重构要类型、协作要类型、大型维护更要类型。
没有 TS,你的项目会是 AI 生成的 “脆弱屎山”;有了 TS,你才能驾驭 AI、高效协作、长期维护,让项目走得稳、走得远。

拓展

shell
# AI 代码生成:类型是 AI 的 “精准上下文”
AI 不懂代码,只懂模式与约束:没有类型,AI 只能瞎猜变量 / 参数类型,把 price(数字)当字符串、把数组传成对象,错误率高、调试时间长 40%+。
TS 类型 = AI “护栏”:接口 / 类型定义给 AI 明确约束,生成代码准确率显著提升,94% LLM 编译错误是类型问题,TS 可在编译阶段拦截。
AI 工具全栈 TS-first:Copilot X、Cursor、Claude Code、Vercel AI SDK、LangChain.js、OpenAI SDK 均默认 TS、类型优先。

# AI 重构:类型安全是 “重构的安全网”
JS 重构 = 赌博:改一个函数签名,不知道影响多少调用处,只能靠测试 / 人肉排查,风险极高。
TS 重构 = 编译器兜底:改接口 / 类型,TS 自动扫描全项目调用链,精确标出所有需修改处,99.5% 相关变更可被发现。
AI 重构依赖类型:AI 做大规模重构(如拆分模块、统一数据结构)时,必须靠类型理解依赖关系;无类型则 AI 重构会引入大量隐蔽 bug。

# 团队协作:类型是 “团队契约”
无类型 = 沟通成本爆炸:新成员靠猜 / 文档理解数据结构,“你传的是啥类型” 成为日常,协作效率低、bug 多。
TS 类型 = 自文档化契约:接口 / 类型即文档,新成员10 分钟看懂数据结构,无需反复沟通。
减少 “协作式 bug”:A 改了参数类型,B 调用处未同步,TS 直接报错,避免线上隐蔽故障。
全栈类型共享:前后端通过 openapi-typescript 共享类型,接口变更自动同步,联调成本降 60%+。

# 大型项目维护:TS 是 “抗熵增利器”
规模定律:项目 >5000 行、团队 >3 人、维护 >6 个月,TS 类型的复利效应显著。
错误率大幅下降:TS 项目平均 bug 率比 JS 40%,调试时间减少 23%,线上类型相关 bug 60%。
长期维护成本低:类型约束减少 “代码熵增”,可读性、可维护性、可扩展性全面提升,避免 “屎山”。
IDE 智能提示:VS Code 基于类型提供精准自动补全、参数提示、错误高亮,开发效率提升 35%+。

# 生态与趋势:2026 已无退路
框架全 TS 优先:Vue3、React、Angular、NestJS、Svelte 均默认 TS、官方文档 TS 先行。
开源项目 TS 化:78%+ 活跃开源项目核心模块用 TS,GitHub TS 已超 Python 成为最热门语言。
TS 6.0+ 新特性:默认严格模式、ESM 优先、增量编译提速 40%、装饰器标准化,开发体验与性能拉满。
AI 时代的必然选择:TS “运行时错误” 提前到 “编译时”,让 AI 生成的代码可信任、可维护、可扩展。