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 生成的代码可信任、可维护、可扩展。