TypeScript 如何做类型提取

做前端开发的人,大概都遇过这种糟心事儿:从后台接口拿了一串数据,打印出来明明有“年龄”字段,写代码的时候却总报“找不到这个属性”的错——都是没做类型校验惹的祸。很多人刚用TypeScript时,会自己对着数据手动敲类型,比如接口返回的用户信息,就写个type User = { name: string; age: number },但只要接口字段变了,就得跟着改,麻烦又容易漏。其实TypeScript自带了“类型提取”的功能,能从已有类型里“薅”出需要的部分,不用重复造轮子,还能保证类型同步。

从已有类型里“揪”出想要的字段

平时写业务,最常用的场景是:后台返回的用户类型里,可能有手机号、地址、会员等级这些后端才关心的字段,但前端表单只需要姓名、邮箱、验证码。总不能再写一遍type FormData = { name: string; email: string; code: string }吧?这时候用Pick就合适了——它就像从购物清单里挑出你要的那几样,不用重新开清单。

TypeScript 如何做类型提取

举个例子:

`typescript

// 后台返回的完整用户类型

type User = {

id: number;

name: string;

email: string;

phone: string;

address: string;

memberLevel: number;

};

// 只提取表单需要的3个字段

type LoginForm = Pick<User, 'name' | 'email' | 'code'>;

`

这样LoginForm就自动包含了那三个字段,要是User里的name变成userName,LoginForm也会跟着变,完全不用手动改,省了好多事。

把不需要的字段“过滤掉”

有时候情况反过来:完整类型里大部分字段都能用,就一两个不需要,这时候不用挑出要的,直接把不用的去掉就行,用Omit就对了。比如还是刚才的User类型,用户中心的基础信息页,不需要id、phone、memberLevel,那直接排除这三个:

`typescript

type UserProfile = Omit<User, 'id' | 'phone' | 'memberLevel'>;

`

UserProfile就剩下name、email、address,比一个个挑字段更省事,尤其当要排除的字段少、要留的多时,Omit比Pick方便。

从数组或Promise里取类型

除了纯对象,开发中还常碰到从接口拿数组、从异步请求拿数据的情况,这时候类型提取也能用上。比如接口返回的是用户列表,类型是Promise<User[]>,想直接拿数组里单个用户的类型,不用再拆包:

`typescript

// 异步接口的返回类型,比如await fetch('/api/users')的结果

type UserListRes = Promise<User[]>;

// 提取数组里单个元素的类型,就像从一袋橘子里拿出单个橘子

type SingleUser = Awaited<UserListRes> extends (infer T)[] ? T : never;

`

这里的Awaited就像拆快递,把Promise的外层拆开,拿到里面的内容;再用infer(可以理解为“推断”)拿到数组里的单个类型,这样就不用自己重新定义SingleUser了,只要User类型不变,这个提取出来的类型就一直对。

实用小技巧:提取函数的返回值

写工具函数时,比如一个格式化日期的函数,formatDate(new Date())返回的是字符串,要是其他地方需要复用这个返回类型,不用再猜它是string,直接用ReturnType提取:

`typescript

function formatDate(date: Date): string {

return date.toLocaleDateString();

}

// 提取函数的返回类型,相当于自动得到string

type DateStr = ReturnType<typeof formatDate>;

`

要是函数以后改了返回类型,比如变成返回字符串加时间戳,DateStr也会跟着变,完全不用手动更新,减少了出错的可能。

其实TypeScript的类型提取,本质就是“复用已有类型,减少重复劳动”,不用再对着接口文档手动敲类型,只要把这些小工具用熟,写代码时既能少写冗余代码,又能靠类型校验减少bug,对前端开发来说真的是提升效率的小妙招。

上一篇 TypeScript 如何用 VS Code 调试