react中typescript定义

目的,更多React类型,在项目中使用有更强的约束性,利于后期维护。

引用React

React17(也就是create-react-app4.0开始默认打开plugin-transform-react-jsx,或者plugin支持不用手动引入React’)之后不用引入React✅

import * as React from 'react'
​
import * as ReactDOM from 'react-dom'

React.FC

React.FunctionComponent=React.FC,显式地定义了返回类型,其他方式是隐式推导的

displayName、propTypes、defaultProps提供了类型检查和自动补全

为children提供了隐式的类型(ReactElement | null)

如果出现类型不兼容问题:还有一个替代品PropsWithChildren,或者显示声明children?: React.ReactNode

type AppProps = {message: string}✅//用type,children会自动有隐式类型
​
//替代品,此时不写下面的React.FC,自动设置 children 类型为 ReactNode
//type AppProps = React.PropsWithChildren<{ message: string }>
​
//这个AppProps默认为空对象{}
const App: React.FC<AppProps> = ({ message, children }) => (
  <div>
    {message}
    {children}
  </div>
)

ReactElement和ReactNode和JSX.Element

React.component<P,S>React.PureComponent<P,S,SS>,这个SSgetSnapshotBeforeUpdate的返回值

类组件的render方法返回ReactNode,而函数组件返回ReactElement

因为ReactNode可以是undefined等更多类型

JSX.Element 是一个 ReactElement,props 和 type的泛型类型是 any。

ReactElement 是一个具有type和props的对象。

JSX.Element ≈ ReactElement ⊂ ReactNode

常用Hooks类型的一些技巧

总结:

useState自动推导,或者传入泛型

useRef自动推导,或者传入泛型,如(<HTMLInputElement | null>)

useEffect固定,返回值只能是undefined和()=>{}

useMemouseCallback 自动推导返回值 从它们返回的值中推断出它们的类型 也可传入泛型多此一举

useCallback 参数必须指定类型,否则ts不会报错,默认指定为 any

使用推导类型作为接口/类型:

const [user] = React.useState({ name: 'sj', age: 32 })//自动推导出类型
const showUser = React.useCallback((obj: typeof user) => {//这里typeof使用推导出的类型✅
    return `My name is ${obj.name}, My age is ${obj.age}`
}, [])

初始状态为null时候,需要显示声明类型:

type User = {name:string}
const [user, setUser] = React.useState<User | null>(null)

useRef的类型:

const ref1 = React.useRef<HTMLInputElement>(null)//ref1.current 是只读的
const ref2 = React.useRef<HTMLInputElement | null>(null)//ref2.current 是可变的✅
​
//可是这样声明貌似不能使用ref来保存setTimeout❌ 
const ref1 = React.useRef<any>(null)//暂时这样吧
​
//一个场景focus
const onButtonClick = () => {
  ref2.current?.focus()//可以使用可选链
}
​
//其他
// 存储div dom
const divRef = React.useRef<HTMLDivElement | null>(null);
​
// 存储button dom
const buttonRef = React.useRef<HTMLButtonElement | null>(null);
​
// 存储a dom
const linkRef = React.useRef<HTMLLinkElement | null>(null);

useMemo和useCallback:

// 自动推断 (value: number) => number
​
const multiply = React.useCallback((value: number) => value * multiplier, [
  multiplier,
])
​
//泛型
//同时也支持传入泛型, useMemo 的泛型指定了返回值类型,useCallback 的泛型指定了参数类型
​
const handleChange = React.useCallback<
  React.ChangeEventHandler<HTMLInputElement>
>(evt => {
  console.log(evt.target.value)
}, [])

自定义Hooks:

//自定义 Hook 的返回值如果是数组类型,TS 会自动推导为 Union 类型,解决:const断言
​
function useLoading() {
    const [isLoading, setState] = React.useState(false)
    const load = (aPromise: Promise<any>) => {
    setState(true)
    return aPromise.then(() => setState(false))
  }
  // 实际需要: [boolean, typeof load] 类型
  // 而不是自动推导的:(boolean | typeof load)[]
    return [isLoading, load] as const //这个hook把isLoading和一个函数返回出去✅
}
​
//大量的自定义 Hook 需要处理,这里有一个方便的工具方法可以处理 tuple 返回值
function tuplify<T extends any[]>(...elements: T) {
  return elements
}
​
...
//[boolean, typeof load]
把上面例子 return [isLoading, load] as const   改为=>   return tuplify(isLoading, load)

defaultProps

推荐方式:使用默认参数值来代替默认属性

看了字节的那个例子,…有点麻烦,还要交叉类型,表示再也不会用defaultProps了

注意的是有这样一个类型React.ComponentProps

const TestComponent = (props: React.ComponentProps<typeof Greet>) => {
  return <h1 />
}

Types or Interfaces

巧用type:

let user = {  name: 'Lucy',  age: 20 }
​
type User = typeof user;✅//user{name:string,age:number}

选择:

  1. 在定义公共 API 时(比如编辑一个库)使用 interface,这样可以方便使用者继承接口
  2. 在定义组件属性(Props)和状态(State)时,建议使用 type,因为 type的约束性更强

区别:

  1. type 类型不能二次编辑,而 interface 可以随时扩展

    type Animal = {
      name: string
    }
    // type类型不支持属性扩展
    // Error: Duplicate identifier 'Animal'
    type Animal = {
      color: string
    }
    //但interface能
    
  2. type 可以定义更多类型,interface只能定义对象

注释:

/**
    * @param color color
*/

Props

常用:

type AppProps = {
    status: 'waiting' | 'success'//字面量加联合类型
        obj: {//列出对象的全部属性,这里也可以写个interface
            id: string
            title: string
        }
      /** array of objects! (common) */
        objArr: {✅//曾几何时,一度不知道这样写== 数组里面多个对象
            id: string
            title: string
        }[]
      /** 携带点击事件的函数 */
        onClick(event: React.MouseEvent<HTMLButtonElement>): void
      //可选
        onClick?: () => void
}

React属性:

export declare interface AppBetterProps {
​
  children: React.ReactNode // 一般情况下推荐使用,支持所有类型 Great
​
  functionChildren: (name: string) => React.ReactNode
​
  style?: React.CSSProperties // 传递style对象
​
  onChange?: React.FormEventHandler<HTMLInputElement>
​
}
  
export declare interface AppProps {
​
  children1: JSX.Element // 差, 不支持数组
​
  children2: JSX.Element | JSX.Element[] // 一般, 不支持字符串
​
  children3: React.ReactChildren // 忽略命名,不是一个合适的类型,工具类类型
​
  children4: React.ReactChild[] // 很好
​
  children: React.ReactNode // 最佳,支持所有类型 推荐使用
​
  functionChildren: (name: string) => React.ReactNode // recommended function as a child render prop type
​
  style?: React.CSSProperties // 传递style对象
​
  onChange?: React.FormEventHandler<HTMLInputElement> // 表单事件, 泛型参数是event.target的类型
​
}

Forms and Events

onChange:

import * as React from 'react'
​
//第一种·
type changeFn = (e: React.FormEvent<HTMLInputElement>) => void//这样定义下面onchange的参数和返回值
​
const App: React.FC = () => {
​
  const [state, setState] = React.useState('')
​
  const onChange: changeFn = e => {
    setState(e.currentTarget.value)
  }
  //第二种,强制使用 @types / react 提供的委托类型
  const onChange: React.ChangeEventHandler<HTMLInputElement> = e => {✅
    setState(e.currentTarget.value)
  }
  
  return (
    <div>
      <input type="text" value={state} onChange={onChange} />
    </div>
  )
}

onSubmit:

const onSubmit = (e: React.SyntheticEvent) => {✅
    e.preventDefault()
    const target = e.target as typeof e.target & {
      password: { value: string }
    } // 类型扩展---表示看不懂---
    const password = target.password.value
}

Operators(运算符)

常用的操作符,常用于类型判断 ---- 只会用常用的 = =

- typeof and instanceof: 用于类型区分
​
- keyof: 获取object的key
​
- O[K]: 属性查找
​
- [K in O]: 映射类型
​
- + or - or readonly or ?: 加法、减法、只读和可选修饰符
​
- x ? Y : Z: 用于泛型类型、类型别名、函数参数类型的条件类型
​
- !: 可空类型的空断言
​
- as: 类型断言
​
- is: 函数返回类型的类型保护

Tips

😥使用查找类型访问组件属性类型

这个场景我理解是:子组件必须接受一个name参数,父组件要把它的props传入子组件,那么父组件要拿到子组件这个必要的类型来定义自己的Iprops

总的来说就是React.ComponentProps<typeof Counter>拿到子组件必要类型,再用交叉类型&,定义自己的Iprops

通过查找类型减少 type 的非必要导出,如果需要提供复杂的 type,应当提取到作为公共 API 导出的文件中。

现在我们有一个 Counter 组件,需要 name 这个必传参数:

// counter.tsx
​
import * as React from 'react'
​
export type Props = {//app要用,所以export😎
  name: string
}
​
const Counter: React.FC<Props> = props => {
  return <></>
}
​
export default Counter

引用它的组件中我们有两种方式获取到 Counter 的参数类型

第一种是通过 typeof 操作符(推荐)

import Counter from './d-tips1'
​
type PropsNew = React.ComponentProps<typeof Counter> & {✅
  age: number//这是app组件要的
}
​
const App: React.FC<PropsNew> = props => {
  return <Counter {...props} />
}
​
export default App

事件处理

通过 interfaceevent 对象进行类型声明编写的话十分浪费时间, React 的声明文件提供了 Event 对象的类型声明

ClipboardEvent<T = Element> 剪切板事件对象
​
DragEvent<T =Element> 拖拽事件对象
​
ChangeEvent<T = Element> Change事件对象
​
KeyboardEvent<T = Element> 键盘事件对象
​
MouseEvent<T = Element> 鼠标事件对象
​
TouchEvent<T = Element> 触摸事件对象
​
WheelEvent<T = Element> 滚轮时间对象
​
AnimationEvent<T = Element> 动画事件对象
​
TransitionEvent<T = Element> 过渡事件对象

事件处理函数类型

当我们定义事件处理函数时有没有更方便定义其函数类型的方式呢?答案是使用 React 声明文件所提供的 EventHandler 类型别名,通过不同事件的 EventHandler 的类型别名来定义事件处理函数的类型

😭信息量直接拉满

//bivarianceHack 为事件处理函数的类型定义,函数接收一个 event 对象,并且其类型为接收到的泛型变量 E 的类型, 返回值为 void
//关于为何是用bivarianceHack而不是(event: E): void,这与strictfunctionTypes选项下的功能兼容性有关。(event: E): void,如果该参数是派生类型,则不能将其传递给参数是基类的函数。
​
type EventHandler<E extends React.SyntheticEvent<any>> = {
​
  bivarianceHack(event: E): void
​
}['bivarianceHack']
​
type ReactEventHandler<T = Element> = EventHandler<React.SyntheticEvent<T>>
​
type ClipboardEventHandler<T = Element> = EventHandler<React.ClipboardEvent<T>>
​
type DragEventHandler<T = Element> = EventHandler<React.DragEvent<T>>
​
type FocusEventHandler<T = Element> = EventHandler<React.FocusEvent<T>>
​
type FormEventHandler<T = Element> = EventHandler<React.FormEvent<T>>
​
type ChangeEventHandler<T = Element> = EventHandler<React.ChangeEvent<T>>
​
type KeyboardEventHandler<T = Element> = EventHandler<React.KeyboardEvent<T>>
​
type MouseEventHandler<T = Element> = EventHandler<React.MouseEvent<T>>
​
type TouchEventHandler<T = Element> = EventHandler<React.TouchEvent<T>>
​
type PointerEventHandler<T = Element> = EventHandler<React.PointerEvent<T>>
​
type UIEventHandler<T = Element> = EventHandler<React.UIEvent<T>>
​
type WheelEventHandler<T = Element> = EventHandler<React.WheelEvent<T>>
​
type AnimationEventHandler<T = Element> = EventHandler<React.AnimationEvent<T>>
​
type TransitionEventHandler<T = Element> = EventHandler<
  React.TransitionEvent<T>
>

Promise 类型

在做异步操作时我们经常使用 async 函数,函数调用时会 return 一个 Promise 对象,可以使用 then 方法添加回调函数。Promise 是一个泛型类型,T 泛型变量用于确定 then 方法时接收的第一个回调函数的参数类型

type IResponse<T> = {
  message: string
  result: T
  success: boolean
}
​
async function getResponse(): Promise<IResponse<number[]>> {//使用泛型✅
  return {
    message: '获取成功',
    result: [1, 2, 3],
    success: true,
  }
}
​
getResponse().then(response => {
  console.log(response.result)
})

泛型参数的组件

这个是通过传入的类型泛型 判断props类型

下面这个组件的name属性都是指定了传参格式,如果想不指定,而是想通过传入参数的类型去推导实际类型,这就要用到泛型。

const TestB = ({ name, name2 }: { name: string; name2?: string }) => {
  return (
    <div className="test-b">
      TestB--{name}
      {name2}
    </div>
  )
}

如果需要外部传入参数类型,只需 ->

type Props<T> = {
  name: T
  name2?: T
}
​
//这样泛型去接✅
const TestC: <T>(props: Props<T>) => React.ReactElement = ({ name, name2 }) => {
  return (
    <div className="test-b">
      TestB--{name}
      {name2}
    </div>
  )
}
​
const TestD = () => {
  return (
    <div>
      //这样传入泛型✅
      <TestC<string> name="123" />
    </div>
  )
}

什么时候使用泛型

需要作用到很多类型的时候,举个🌰

const id = arg => arg//bad
​
//bad 重复定义了
type idBoolean = (arg: boolean) => boolean
type idNumber = (arg: number) => number
type idString = (arg: string) => string
// ...
​
//good✅✅✅✅✅✅
function id<T>(arg: T): T {
  return arg
}
// 或✅✅✅✅✅✅
const id1: <T>(arg: T) => T = arg => {
  return arg
}

😭需要被用到很多地方的时候,比如常用的工具泛型 Partial

//功能是将类型的属性变成可选, 注意这是浅 Partial。
type Partial<T> = { [P in keyof T]?: T[P] }
//如果需要深 Partial 我们可以通过泛型递归来实现
type DeepPartial<T> = T extends Function
  ? T
  : T extends object
  ? { [P in keyof T]?: DeepPartial<T[P]> }
  : T
type PartialedWindow = DeepPartial<Window>

TS内置类型

interface Old {
    name: string
    age?: number
    gender: string
}

interface Old2 extends Old {
    six: number
}

const a: Old2 = {
    six: 1,
    name:'1',
    gender:'1'
}

type New0 = Partial<Old>

type New1 = Omit<Old, 'name'>

type New2 = Pick<Old, 'age'>

type New3 = Required<Old>

type New4 = Readonly<Old>

type A = number | string | boolean

type B = number | boolean

type New5 = Exclude<A, B>//排除

type New6 = Extract<A, B>//交集

type New7 = Record<keyof Old, string>//全变为string

总结一下可能常用的

const ref2 = React.useRef<HTMLInputElement | null>(null)
​
return [isLoading, load] as const //自定义hooks
​
type AppProps = {
    status: 'waiting' | 'success'//字面量string类型
    objArr: {//数组包对象类型
        id: string
        title: string
    }[]
}
​
children: React.ReactNod//比较全面
​
React.CSSProperties // 传递style对象
​
type changeFn = (e: React.FormEvent<HTMLInputElement>) => void
​
const onSubmit = (e: React.SyntheticEvent) => {}
​
type Iobj = {//定义对象
    [type: string]: string
}

//使用泛型==
const TestC: <T>(props: Props<T>) => React.ReactElement = ({ name, name2 }) => { return }
<TestC<string> name="123" />

参考

React + TypeScript实践

React+Typescript最佳实践

React中常见的TypeScript定义使用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值