typescript在实际场景中的应用

前言
typescript只按照教程去学习,好像只会一些string、number等基本类型的使用。实际编程中,似乎总有一些场景不知道怎么去写类型,并且难以找到例子去学习。
本文列举的都是实际项目中用到的类型定义技巧,或许你能从中找到一些灵感。
实际场景中的TS类型应用
避免类型的重复定义
通常我们针对一个Table数据会定义如下类型Item,针对不同场景,需要重新定义某些属性,下面的一些TS内置类型,可以方便的实现:
/** 定义基本的数据项 */ type Item = { id: number; name: string; age: string; address?: string; }; /** 列表数据,泛型的基本用法,也可以Item[] */ type List = Array<Item>; /** 新增时表单数据,无id,使用Omit移除属性 */ type FormDataAdd = Omit<Item, 'id'>; /** 修改时表单数据,只可修改address,使用Pick挑选属性 */ type FormDataModify = Pick<Item, 'id' | 'address'>; const data: Item = { id: 1, name: 'xx', age: 18, address: 'xxx', }; /** 当确定address必有值,使用NonNullable转换一下 */ const addressMustExist = data.address as NonNullable<Item['address']>; /** 或者使用非空断言 */ const addressMustExist1 = data.address!; /** 查询参数,使用Partial将属性转为可选,使用&扩展属性 */ type Query = Partial<List> & { current: number; pageSize: number; }; /** 定义一个通用的service,泛型T 表示数据项,U 表示查询参数 */ type Service<T, U> = (params: U) => Promise<{ data: T[]; totalItem: number; }>; /** 运用 */ type PageService = Service<Item, Query>;
组件的二次封装

  • 基于antd的Input组件,封装自定义的MyInput
  • MyInput需要支持传入Input的所有props,除了disabled
  • disabled属性是根据myCustomProps动态计算的


主要使用了extends继承原有属性,Omit剔除部分属性
import { Input, InputProps } from 'antd'; import { useMemo } from 'react'; interface IProps extends Omit<InputProps, 'disabled'> { myCustomProps?: string; } export default function MyInput(props: IProps) { const { myCustomProps, ...restInputProps } = props; const computedDisabled = useMemo(() => { if (myCustomProps === 'xxx') { return true; } return false; }, [myCustomProps]); return <Input {...restInputProps} disabled={computedDisabled} />; }
字符串格式限制

  • 写一个range方法,判断一个数字是否在某个区间内
  • 参数interval为string类型,如"[1,5]"表示x>=1且x<=5,"(2,3]"表示x>2且x<=3


校验了字符串的头尾字符限制,不过如果还要校验中间数字的话,还是需要代码逻辑去判断
代码演示
function range(num:number, interval: `${'[' | '('}${string}${')' | ']'}`) { // 实现省略... }
ahooks的useAntdTable二次封装
useAntdTable用起来很方便,封装了常用的 Ant Design Form 与 Ant Design Table 联动逻辑,但是它对service数据格式有固定要求:

  1. service 接收两个参数,第一个参数为分页数据 { current, pageSize },第二个参数为表单数据。
  2. service 返回的数据结构为 { total: number, list: Item[] }


假如我们的service定义如下:
type Service<T> = (params: { currentPage: number; pageSize: number }) => Promise<{ success: boolean; data: { data: T[]; totalItem: number; }; }>;
使用起来就要做一些逻辑转换了:
const myService: Service = function () { return new Promise((resolve) => { resolve({ success: true, data: { data: [], totalItem: 0, }, }); }); }; const { tableProps } = useAntdTable(async (params) => { const res = await myService({ currentPage: params.current, pageSize: params.pageSize, }); return { list: res.data.data, total: res.data.totalItem, }; });
如果我们定义一个useMyAntdTable,service可以在内部转换后给useAntdTable,岂不是方便很多,但是类型要怎么处理呢?
先看的定义吧
declare const useAntdTable: <TData extends Data, TParams extends Params>(service: Service<TData, TParams>, options?: AntdTableOptions<TData, TParams>) => AntdTableResult<TData, TParams>;
关键点就在于泛型参数的处理:<TData extends Data, TParams extends Params>,直接贴代码:
import { useAntdTable } from 'ahooks'; import { AntdTableOptions, Data, Params } from 'ahooks/lib/useAntdTable/types'; /** 我们的返回数据类型 */ export type MyData = { success: boolean; data: { list: any[]; totalItem: number; }; }; /** 我们的service入参 */ export type MyParams = { currentPage: number; pageSize: number; [key: string]: any; }; /** 定义接受的service类型,extends关键词保证了service符合我们的定义约束 */ type MyService<TData extends MyData, TParams extends MyParams> = (args: TParams) => Promise<TData>; /** 获取Promise的返回值类型,这里用到了infer,通常用来搭配泛型来获取某个具体的类型 */ type PromiseType<T> = T extends Promise<infer U> ? U : never; /** 结合useAntdTable的类型定义,来修改我们要的类型 */ function useDrsAntdTable<TData extends MyData, TParams extends MyParams>( service: MyService<TData, TParams>, options: AntdTableOptions<Data, Params> = {}, ) { type returnType = PromiseType<ReturnType<typeof service>>; /** * 我们需要把对应类型也做转换,手动赋给useAntdTable的泛型参数,让TS识别到 * 通常使用useAntdTable时并不需要指定这个泛型,是因为TS可以从service中推断出来 */ type TransformData = { list: returnType['data']['list']; total: number; }; const hooks = useAntdTable<TransformData, Params>(async ({ current, pageSize }, params) => { const { data } = await service({ currentPage: current, pageSize, ...params, }); return { total: data.totalItem, list: data.list, }; }, options); return hooks; } export default useDrsAntdTable;
使用看看,代码简洁多了,dataSource类型也能正确提示出来:

原文链接: https://juejin.cn/post/73783528
  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值