TypeScript 如何做类型映射
TypeScript 之所以受前端开发者欢迎,除了能帮我们提前发现代码错误,还有一个很实用的功能:能基于已有类型快速生成新类型,这个过程就是大家常说的“类型映射”。很多刚接触的人会觉得这是个很复杂的概念,但其实用日常的例子就能讲明白。
先搞懂:什么是类型映射?
假设你有一个产品列表,每个产品都有id(编号)、name(名称)、price(价格)三个固定属性。用TypeScript定义这个产品的类型,会写成这样:

`typescript
type Product = {
id: number;
name: string;
price: number;
};
`
如果这时候你需要一个“只需要产品编号的简化类型”,难道要再写一遍所有属性?总不能每次要改都重新敲一遍吧?这就像你有一张全家福照片,现在需要一张只剪你自己的单人照,不用重新拍一张全家,只要对原照片做裁剪——类型映射就是这么个“裁剪已有类型”的工具,能快速派生出新类型,不用重复写代码。
最基础的类型映射:用`keyof`取属性名
要做映射,首先得知道怎么把一个类型的所有属性名拿出来。TypeScript提供了keyof操作符,你可以把它理解成“拿钥匙”,每个属性名就像一把钥匙,keyof Product拿到的就是'id' | 'name' | 'price'这几个属性名组成的集合,相当于把产品类型的“属性清单”列出来了。
比如你想把产品类型的所有属性都变成可选的,就可以结合keyof和映射语法写:
`typescript
type PartialProduct = {
[K in keyof Product]?: Product[K];
};
`
这里的[K in keyof Product]就是映射的核心:K是一个变量,会依次代表keyof Product里的每一个属性名,比如先代表'id',再代表'name',最后代表'price',整个结构就是“给原类型的每个属性都加个可选标记”,效果和手动写id?: number; name?: string; price?: number完全一样,却省了好多代码。
更实用的场景:把属性全改成只读或必选
除了可选属性,日常开发里还有两个高频需求:要么不让别人随便改属性(只读),要么把之前不小心设成可选的属性改回必选。这两种也能靠类型映射轻松实现。
比如你做了一个用户注册表单,一开始把所有输入项都设成了可选的(怕用户漏填),后来要提交的时候,需要把所有必填项都改成必选,就可以用Required这个内置的映射类型:
`typescript
type RegisterForm = {
username?: string;
email?: string;
password?: string;
};
type RequiredForm = Required<RegisterForm>;
`
这个Required其实就是官方帮你写好的映射,相当于“把原类型的所有?都去掉,变成必选属性”,效果和手动写一遍所有带?的属性再去掉?一模一样。
反过来,如果想让类型的属性不能被修改,比如产品详情页的信息不能随便改,就用Readonly:
`typescript
type ReadonlyProduct = Readonly<Product>;
// 这时候改属性会报错:Cannot assign to 'price' because it is a read-only property
const phone: ReadonlyProduct = { id: 1, name: '手机', price: 2999 };
phone.price = 3999; // 会直接被TypeScript拦住,避免写错
`
自定义映射:按需改属性的类型或可见性
如果官方的内置映射不够用,还能自己写更灵活的。比如你有一个接口返回的用户类型,所有属性都是字符串,但后台要求年龄的属性必须是数字,这时候可以自定义一个映射,把指定属性的类型改掉:
`typescript
type ApiUser = {
id: string;
name: string;
age: string;
};
type CustomUser = {
[K in keyof ApiUser]: K extends 'age' ? number : ApiUser[K];
};
// 生成的类型就是{ id: string; name: string; age: number },自动把年龄转成数字了
`
这个例子里,K extends 'age'就是判断当前属性是不是年龄,是的话就把类型改成number,其他属性保持原样,完全不用手动改每个属性。
说白了,类型映射就是TypeScript给类型做的“批量处理工具”,不用每次写新类型都重复劳动,就像你用Excel批量改格式,比一个个单元格改高效多了。只要记住keyof拿属性清单、[K in ...]遍历属性,再结合简单的类型判断,就能应付大部分日常开发里的类型需求。