Typescript类型常用使用技巧1

测试员
• 阅读 2490

枚举类型怎么定义?

有这么一个常量FieldTypes,代表表单项类型,是输入框还是开关还是其他等等,只列举两个

enum FieldTypes {
  INPUT = 'input',
  SWITCH = 'switch'
  // ...
}

此时子组件需要接收一个typeprops,类型的值为FieldTypes中的定义的值,即'input' | 'switch'等等

解法一:

typescript中enum可以作为类型约束某个变量,如果这么做,那么给这个propstype赋值时,必须从enum中去取,保证了数据的来源以及数据类型的统一性,缺点是在某些场景下不是特别灵活

const _type: FieldTypes = FieldTypes.INPUT // ✅ correct
const _type: FieldTypes = 'input' // ❎ error

解法二:

使用只读的 object 来替代enum的作用

const FieldTypesObj = {
  INPUT: 'input',
  SWITCH: 'switch'
} as const // <-- 关键技巧 1: as const

type Type = typeof FieldTypesObj // <-- 关键技巧 2: 用 typeof 关键字从 FieldTypesObj 反向创建同名类型

const _type1: Type[keyof Type] = 'input' // ✅ correct
const _type2: Type[keyof Type] = FieldTypes.SWITCH // ✅ correct

常量断言(语法写作 as const)是 TypeScript 3.4发布的新特性,这里对它进行简单的解释:

先看下面例子:

let str = 'ghostwang' // str: string
const str = 'ghostwang' // str: 'ghostwang'
let str = 'ghostwang' as const // str: 'ghostwang'

const obj1 = { name: 'ghostwang' } // const obj: { name: string; }
const obj2 = { name: 'ghostwang' } as const // const obj: { readonly name: "ghostwang"; }

const arr = [1, 2] // const arr: number[]
const arr2 = [1, 2] as const // const arr: readonly [1, 2]

看出区别来了么,使用as const会告诉编译器为表达式推断出它能推断出的最窄或最特定的类型。如果不使用它,编译器将使用其默认类型推断行为,这可能会导致更广泛或更一般的类型,并且此时他的属性是只读的(当然还是有办法能修改)

事件类型该怎么定义?

相信有人在定义事件的时候有时候不知道怎么去定义,例如下面的场景,div点击时阻止冒泡

const onClick = (e)=>{// 这里e的类型是什么?
    e...//
}

<div onClick={onClick}><div>

这里很多会定义为e:any 然后冒泡的函数是啥,阻止冒泡stop什么什么,再去查一下。

其实想一想,如果能知道<div>的props的类型定义,我可以直接定义onClick这个函数,从而e就有类型了,不仅可以检查代码,还可以得到友好的提示。JSX提供了这样一个查询组件props的泛型:

// 获得div标签的props的类型 (内置组件,例如 div、a、p、input等等)

const DivPropsType = JSX.IntrinsicElements<'div'>

// 获得自定义组件的props

const CustomPropsType = JSXElementConstructor<CustomComponent>

为了减少记忆的负担,React对这2个泛型又进行了一步包装:

type ComponentProps<T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>> =

        T extends JSXElementConstructor<infer P>

            ? P

            : T extends keyof JSX.IntrinsicElements

                ? JSX.IntrinsicElements[T]

                : {};

所以上面的事件类型定义就变得非常简单了:

import { ComponentProps } from 'react'

const onClick: ComponentProps<'div'>['onClick'] = (e)=>{// e的类型被自动推导

    e.//会得到代码提示

}

<div onClick={onClick}><div>

同样,我们在引入自定义组件时,也不需要单独引入它的props类型,直接使用这个泛型即可:

import { type ComponentProps } from 'react'

const onClick: ComponentProps<typeof Custom>['onClick'] = (e)=>{// e的类型被自动推导

    e.//会得到代码提示

}

<Custom onClick={onClick}></Custom>

怎样定义一个字符串类型,既有枚举,又可以自由输入?

这种应用场景非常常见,我这样写标题,可能表达得不清晰,举一个例子:

我需要定义一个color类型,在开发者输入时,可以提示输入 "red" 和 "blue",但是除了red和blue也可以自由输入其他字符串

Typescript类型常用使用技巧1

但是这样定义类型的话,除了red和blue,其他都输入不了。都会报错。

如果加上string,直接什么都不提示了

Typescript类型常用使用技巧1

所以需要定义一个既包含red和blue,又包含除了red和blue之外的字符串

Typescript类型常用使用技巧1

我输入了white,也不会报错

Typescript类型常用使用技巧1

ref的类型该怎么定义?

ref的应用场景常用来储存一些改变不会引起重新渲染、用来引用forwardRef的组件、引用内置组件使用。

import { useRef } form 'react'



const valueRef = useRef<number>(0)

这种方式定义的valueRef的类型是MutableRefObject 可变的引用对象

除了这种方式,还有一种不可变的,对应的类型是RefObject 只读的引用对象 感觉这俩就是const和let一样

看一下区别

import { useRef, type MutableRefObject, type RefObject } form 'react'



const valueRef1: MutableRefObject<number> = useRef(0)

const valueRef2: RefObject<number> = useRef(0)



valueRef1.current = 1; // 正常

valueRef2.current = 1; // 报错,不能赋值: 无法分配到 "current" ,因为它是只读属性。

所以我们在定义几种场景时,应区分是手动赋值还是自动赋值,并使用不同的类型

例如用来封装一个useUUID的hook

import { useRef, type RefObject } form 'react'



// 定义只读的ref

export const useUUID = ()=>{

    const uuidRef: RefObject<string> = useRef('uuid' + Date.now().toString(16));

    

    return uuidRef.current

}

例如引用一个div的ref

import { useRef, type RefObject } form 'react'



const divRef: RefObject<HTMLDivElement> = useRef();



<div ref={divRef}></div>

forwardRef的类型该怎样定义以及引用时类型该怎样定义?

根据官方的推荐,在定义forwardRef时,将类型定义在高阶函数中(注意⚠️props的类型和ref类型位置相反)

const ComA = forwardRef<{dddd: string}, {age?: number}>((props, ref)=>{
  useImperativeHandle(ref, ()=>{
    return {
      dddd: "1"
    }
  })
  return <div></div>
})

在引入时,typeof ComA 得到的是一个ref和props的交叉类型,所以只需访问出ref的类型即可

const ComB = ()=>{
  const tRef: ComponentProps<typeof ComA>['ref'] = useRef();
  
  return <ComA ref={tRef}></ComA>
}

React在此处设计的类型是ComponentProps<typeof ComA>['ref'] 返回的是一个React.Ref泛型

type Ref<T> = RefCallback<T> | RefObject<T> | null;

正好兼容了ref的3种情况,使用函数接收(createRef),useRef引用,和初始空值。而且还是个只读的ref 👍

然后访问tRef时,此时tRef即是已经收窄的类型,具有友好的提示和取值限制。

Typescript类型常用使用技巧1

写在最后的话

至此,结合最近组内小伙伴分享的一些ts类型使用技巧,在此总结并分享给更多的人,感谢阅读~

Typescript类型常用使用技巧1

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Easter79 Easter79
3年前
sql注入
反引号是个比较特别的字符,下面记录下怎么利用0x00SQL注入反引号可利用在分隔符及注释作用,不过使用范围只于表名、数据库名、字段名、起别名这些场景,下面具体说下1)表名payload:select\from\users\whereuser\_id1limit0,1;!(https://o
皕杰报表(关于日期时间时分秒显示不出来)
在使用皕杰报表设计器时,数据据里面是日期型,但当你web预览时候,发现有日期时间类型的数据时分秒显示不出来,只有年月日能显示出来,时分秒显示为0:00:00。1.可以使用tochar解决,数据集用selecttochar(flowdate,"yyyyMMddHH:mm:ss")fromtablename2.也可以把数据库日期类型date改成timestamp
Karen110 Karen110
3年前
​一篇文章总结一下Python库中关于时间的常见操作
前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
Java泛型一览笔录
1、什么是泛型?泛型(Generics)是把类型参数化,运用于类、接口、方法中,可以通过执行泛型类型调用分配一个类型,将用分配的具体类型替换泛型类型。然后,所分配的类型将用于限制容器内使用的值,这样就无需进行类型转换,还可以在编译时提供更强的类型检查。2、泛型有什么用?泛型主要有两个好处:(1)消除显
Wesley13 Wesley13
3年前
ThinkPHP 根据关联数据查询 hasWhere 的使用实例
很多时候,模型关联后需要根据关联的模型做查询。场景:广告表(ad),广告类型表(ad\_type),现在需要筛选出广告类型表中id字段为1且广告表中status为1的列表先看关联的设置部分 publicfunctionadType(){return$thisbelongsTo('A
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这