项目中使用ts的一些技巧

  项目上使用ts一年多了,一边写,一边看,总结了一些小技巧,写了一些分享给大家,如果对你有所帮助就转评赞三连来一个,那么我们开始今天的正题;

(1)、type联合类型简化

  我们先看下面一个联合类型案例:

// 大北京疫情,家中蔬菜总量类目
const vegetablesNum = {
  tomatoes: 12,
  radishs: 10,
  cabbages: 1
};
// 定义单类目
type Vegetables = | 
{
  tomatoes: number
} | {
  radishs: number
} | {
  cabbages: number
};
// 今天我们只吃其中一种或多种,
const cookie: Vegetables = {
  tomatoes: 10,
}

  上面的type + |方式实现了一个不为空{}任意key组合的对象,也许很多人在想是否可以通过interface + ?可选属性方式实现,答案是不可以的,因为这样处理总会出现{}或者固定key的情况;

  然而上述方案有个致命问题,如果vegetablesNum里面新增的其他的蔬菜,我们的type Vegetables就需要跟着改动,这样Vegetables这个类型定义就显得很笨;所以我们需要优化这个Vegetables类型,让他适配更多的情况,具体如下:

// 大北京疫情,家中蔬菜总量类目
const vegetablesNum = {
  tomatoes: 12,
  radishs: 10,
  cabbages: 1
};

type NewVegetables = {
  [K in keyof typeof vegetablesNum]: {
    [K2 in K]: typeof vegetablesNum[K2]
  }
}[keyof typeof vegetablesNum]

const cookie2: NewSingleVegetable = {
  tomatoes: 123,
  cabbages: 123
}

  上面实现的NewVegetables可以随时满足vegetablesNum数据结构的变化,已经很nice了,可是如果有一天隔壁工位老王,说你这个类型很nice,我要用,开始c + v? 好像无聊的代码又增加了一份,因此我们考虑一下泛型思维解决重复劳动?看一看具体办法吧:

// 大北京疫情,家中蔬菜总量类目
const vegetablesNum = {
  tomatoes: 12,
  radishs: 10,
  cabbages: 1
};

const animals = {
  pig: 20,
  monkey: 50,
}

type SingleRecord<T = Record<string, any>> = {
  [K in keyof T]: {
    [R in K]: T[R]
  }
}[keyof T]

const cookie3: SingleRecord<typeof vegetablesNum> = {
  tomatoes: 123
}
                            
const monkeys: SingleRecord<typeof animals> = {
  monkey: 50                            
}

  这样老王就可以开心使用摸鱼技巧了;

(2)、extends与泛型优化你的函数

  在实际情况中,我们写一些通用方法,为了通用性放宽类型限制,虽然方便了,但是问题在与ts对于他的语法检测也会随之放宽,有一些意想不到的错误出现,如下代码所示:

let obj = {
  name: 'kanade',
  age: 20
}

function getInfo(obj: Record<string, any>, key: string) {
  return obj[key]
}

getInfo(obj, 'age1')

  getInfo传入非obj的key值错误,会有意想不到undefined取值出现,遇到这样的问题,怎么能让我们函数看起来匹配不同类型数据,还有良好的语法纠错提示呢?(不要在乎示例,你可能会说直接obj.name,放过他吧,他还只是一个example~~)
下面有请我们的extends和泛型同学登场,来改造我们的函数

let obj = {
  name: 'kanade',
  age: 20
}

function getInfoNew<T, K extends keyof T>(obj: T, key: K) {
  return obj[key]
}

getInfoNew(obj, 'age')

  效果图,这么良好的提示你们爱了么
image.png

(3)、as类型断言 + keyof 让遍历更加友好

  我们看一看蔬菜大礼包的例子

const vegetables = {
  tomatoes: 12,
  radishs: 10,
  cabbages: 1
}

Object.keys(vegetables).forEach((key) => {
  console.log(`${key}-数量-${vegetables[key]}`)
})

  显然获取蔬菜数量vegetables[key]ts语法检测报错,原因很简单,Object.keys(obj)返回的是一个string[]类型,然而vegetables里面 的key值是"tomatoes" | "radishs" | "cabbages"string[] > "tomatoes" | "radishs" | "cabbages",类型定义的超出了他的可使用范围,所以最合适的办法就是,我们全局定义一个通用的获取Object.key的方法,让他返回对象key值的联合类型的数组,具体如下:

const vegetables = {
  tomatoes: 12,
  radishs: 10,
  cabbages: 1
}

export const getObjectKey = <T>(obj: T): (keyof T)[] => {
  return Object.keys(obj) as (keyof T)[]
}

getObjectKey(vegetables).forEach((key) => {
  console.log(`${key}-数量-${vegetables[key]}`)
})

  看看实际效果,很清爽的提示~
image.png

(4)、Pick 让类型可以复用

  在实际项目中,我们有时候后需要从一个已知的类型中生成一个子集内容来使用,如下所示:

interface Info {
  name: string
  age: number
  addr: string
  phoneNum: number
  hobby: string
}
// 如果我们仅仅需要个人名字 +  爱好
interface PersonHobby {
  name: string
  hobby: string
}

  这种写法当然是没有问题的,当然不是最高明的写法,我们可以通过ts提供的Pick类型函数进行改造,具体如下:

interface Info {
  name: string
  age: number
  addr: string
  phoneNum: number
  hobby: string
}
// 如果我们仅仅需要个人名字 +  爱好
interface PersonHobby {
  name: string
  hobby: string
}
// 类型函数
type NewPersonHobby = Pick<Info, 'name' | 'hobby'>

  对比一下,用一行代码解决了四行代码做的事儿,是不是显得你很帅~

(5)、关于react中的组件类型获取办法

  react项目,我们使用antd组件库,定义组件方法是不是经常command进去查阅类型定义,然后复制引入一些类型定义复杂的方式,最后满足了类型校验,最后一看代码一坨~

import React from 'react'

import { Table, TablePaginationConfig } from 'antd'
import { FilterValue, SorterResult, TableCurrentDataSource } from 'antd/lib/table/interface'


const Demo: React.FC = () => {
  const onTableChange = (
    pagination: TablePaginationConfig,
    filters: Record<string, FilterValue | null>,
    sorter: SorterResult<any> | SorterResult<any>[],
    extra: TableCurrentDataSource<any>
  ) => {
    console.log(pagination, filters, sorter, extra)
  }

  return <Table dataSource={[]} onChange={onTableChange}></Table>
}

  那么我们又什么简单方法么?当然有,之前我也是按照这样的笨方法去做的,直到有一天发现了React.ComponentProps这个工具函数,翻生码农把歌唱~,走一波

import React from 'react'

import { Table } from 'antd'


const Demo: React.FC = () => {
  // const onTableChange = (
  //   pagination: TablePaginationConfig,
  //   filters: Record<string, FilterValue | null>,
  //   sorter: SorterResult<any> | SorterResult<any>[],
  //   extra: TableCurrentDataSource<any>
  // ) => {
  //   console.log(pagination, filters, sorter, extra)
  // }

  const onTableChange: React.ComponentProps<typeof Table>['onChange'] = (pagination, filters, sorter, extra) => {
    console.log(pagination, sorter, filters, extra)
  }

  return <Table dataSource={[]} onChange={onTableChange}></Table>
}

  通过这样的方式是不是简单很多,也无需balabala引入一堆东西来去做~~

  本期我就先写这些项目上常使用的一些ts类型技巧,喜欢的朋友们,可以点赞、收藏、关注三连走一波,感谢感谢~

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值