TypeScript2.8+ 版本编写 React 组件的姿势总结

2018-09-2812:26:15WEB前端开发Comments3,058 views1字数 11880阅读模式

TypeScript 是 JS 类型的超集,并支持了泛型、类型、命名空间、枚举等特性,弥补了 JS 在大型应用开发中的不足,那么当 TypeScript 与 React 一起使用会碰撞出怎样的火花呢?接下来让我们一起探索在 TypeScript2.8+ 版本中编写 React 组件的姿势。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

前言

TypeScript2.8+ 版本编写 React 组件的姿势总结

近几年前端对 TypeScript 的呼声越来越高,Ryan Dahl 的新项目 Deno 中 TypeScript 也变成了一个必须要会的技能,知乎上经常见到像『自从用了 TypeScript 之后,再也不想用 JavaScript 了』、『只要你用过 ES6,TypeScript 可以几乎无门槛接入』、『TypeScript可以在任何场景代替 JS』这些类似的回答,抱着听别人说不如自己用的心态逐渐尝试在团队内的一些底层支持的项目中使用 TypeScript。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

使用 TypeScript 的编程体验真的是爽到爆,当在键盘上敲下 . 时,后面这一大串的提示真的是满屏幕的幸福,代码质量和效率提升十分明显,再也不想用 JavaScript 了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

在单独使用 TypeScript 时没有太大的坑,但是和一些框架结合使用的话坑还是比较多的,例如使用 React、Vue 这些框架的时候与 TypeScript 的结合会成为一大障碍,需要去查看框架提供的 .d.ts 的声明文件中一些复杂类型的定义。本文主要聊一聊与 React 结合时经常遇到的一些类型定义问题,阅读本文建议对 TypeScript 有一定了解,因为文中对于一些 TypeScript 的基础的知识不会有太过于详细的讲解。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

编写第一个 TSX 组件


import React from 'react'
import ReactDOM from 'react-dom'

const App = () => {

 return (

  <div>Hello world</div>

 )

}



ReactDOM.render(<App />, document.getElementById('root')

复制代码

上述代码运行时会出现以下错误文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

  • Cannot find module 'react'
  • Cannot find module 'react-dom'

错误原因是由于 ReactReact-dom 并不是使用 TS 进行开发的,所以 TS 不知道 ReactReact-dom 的类型,以及该模块导出了什么,此时需要引入 .d.ts 的声明文件,比较幸运的是在社区中已经发布了这些常用模块的声明文件 DefinitelyTyped文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

安装 ReactReact-dom 类型定义文件

使用 yarn 安装

yarn add @types/react

yarn add @types/react-dom

复制代码
使用 npm 安装

npm i @types/react -s

npm i @types/react-dom -s

复制代码

有状态组件开发

我们定义一个 App 有状态组件,propsstate 如下。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

Props
props类型是否必传
colorstring
sizestring
State
props类型
countstring

使用 TSX 我们可以这样写文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


import * as React from 'react'

interface IProps {
  color: string,
  size?: string,
}
interface IState {
  count: number,
}
class App extends React.Component<IProps, IState> {
  public state = {
    count: 1,
  }
  public render () {
    return (
      <div>Hello world</div>
    )
  }
}

复制代码

TypeScript 可以对 JSX 进行解析,充分利用其本身的静态检查功能,使用泛型进行 PropsState 的类型定义。定义后在使用 this.statethis.props 时可以在编辑器中获得更好的智能提示,并且会对类型进行检查。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

那么 Component 的泛型是如何实现的呢,我们可以参考下 React 的类型定义文件 node_modules/@types/react/index.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

在这里可以看到 Component 这个泛型类, P 代表 Props 的类型, S 代表 State 的类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


class Component<P, S> {

    readonly props: Readonly<{ children?: ReactNode }> & Readonly<P>;

    state: Readonly<S>;

}

复制代码

Component 泛型类在接收到 PS 这两个泛型变量后,将只读属性 props 的类型声明为交叉类型 Readonly<{ children?: ReactNode }> & Readonly<P>; 使其支持 children 以及我们声明的 colorsize文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

通过泛型的类型别名 Readonlyprops 的所有属性都设置为只读属性。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

Readonly 实现源码 node_modules/typescript/lib/lib.es5.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

由于 props 属性被设置为只读,所以通过 this.props.size = 'sm' 进行更新时候 TS 检查器会进行错误提示,Error:(23, 16) TS2540: Cannot assign to 'size' because it is a constant or a read-only property文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

防止直接更新 state

React的 state 更新需要使用 setState 方法,但是我们经常误操作,直接对 state 的属性进行更新。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


this.state.count = 2

复制代码

开发中有时候会不小心就会写出上面这种代码,执行后 state 并没有更新,我们此时会特别抓狂,心里想着我哪里又错了?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

现在有了 TypeScript 我们可以通过将 state ,以及 state 下面的属性都设置为只读类型,从而防止直接更新 state文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


import * as React from 'react'

interface IProps {
  color: string,
  size?: string,
}
interface IState {
  count: number,
}
class App extends React.PureComponent<IProps, IState> {
  public readonly state: Readonly<IState> = {
    count: 1,
  }
  public render () {
    return (
      <div>Hello world</div>
    )
  }
  public componentDidMount () {
    this.state.count = 2
  }
}
export default App

复制代码

此时我们直接修改 state 值的时候 TypeScript 会立刻告诉我们错误,Error:(23, 16) TS2540: Cannot assign to 'count' because it is a constant or a read-only property.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

无状态组件开发

Props
props类型是否必传
childrenReactNode
onClickfunction
SFC类型

在 React 的声明文件中 已经定义了一个 SFC 类型,使用这个类型可以避免我们重复定义 childrenpropTypescontextTypesdefaultPropsdisplayName 的类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

实现源码 node_modules/@types/react/index.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


type SFC<P = {}> = StatelessComponent<P>;
interface StatelessComponent<P = {}> {
    (props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;
    propTypes?: ValidationMap<P>;
    contextTypes?: ValidationMap<any>;
    defaultProps?: Partial<P>;
    displayName?: string;
}

复制代码

使用 SFC 进行无状态组件开发。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


import { SFC } from 'react'
import { MouseEvent } from 'react'
import * as React from 'react'
interface IProps {
  onClick (event: MouseEvent<HTMLDivElement>): void,
}
const Button: SFC<IProps> = ({onClick, children}) => {
  return (
    <div onClick={onClick}>
      { children }
    </div>
  )
}
export default Button

复制代码

事件处理

我们在进行事件注册时经常会在事件处理函数中使用 event 事件对象,例如当使用鼠标事件时我们通过 clientXclientY 去获取指针的坐标。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

大家可以想到直接把 event 设置为 any 类型,但是这样就失去了我们对代码进行静态检查的意义。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


function handleEvent (event: any) {
  console.log(event.clientY)
}

复制代码

试想下当我们注册一个 Touch 事件,然后错误的通过事件处理函数中的 event 对象去获取其 clientY 属性的值,在这里我们已经将 event 设置为 any 类型,导致 TypeScript 在编译时并不会提示我们错误, 当我们通过 event.clientY 访问时就有问题了,因为 Touch 事件的 event 对象并没有 clientY 这个属性。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

通过 interfaceevent 对象进行类型声明编写的话又十分浪费时间,幸运的是 React 的声明文件提供了 Event 对象的类型声明。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

Event 事件对象类型

常用 Event 事件对象类型:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

  • 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> 过渡事件对象

实例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


import { MouseEvent } from 'react'

interface IProps {

  onClick (event: MouseEvent<HTMLDivElement>): void,
}

复制代码

MouseEvent 类型实现源码 node_modules/@types/react/index.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


interface SyntheticEvent<T = Element> {
        bubbles: boolean;
        /**
         * A reference to the element on which the event listener is registered.
         */
        currentTarget: EventTarget & T;
        cancelable: boolean;
        defaultPrevented: boolean;
        eventPhase: number;
        isTrusted: boolean;
        nativeEvent: Event;
        preventDefault(): void;
        isDefaultPrevented(): boolean;
        stopPropagation(): void;
        isPropagationStopped(): boolean;
        persist(): void;
        // If you thought this should be `EventTarget & T`, see https://github.com/DefinitelyTyped/DefinitelyTyped/pull/12239
        /**
         * A reference to the element from which the event was originally dispatched.
         * This might be a child element to the element on which the event listener is registered.
         *
         * @see currentTarget
         */
        target: EventTarget;
        timeStamp: number;
        type: string;
}



interface MouseEvent<T = Element> extends SyntheticEvent<T> {
        altKey: boolean;
        button: number;
        buttons: number;
        clientX: number;
        clientY: number;
        ctrlKey: boolean;
        /**
         * See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method.
         */
        getModifierState(key: string): boolean;
        metaKey: boolean;
        nativeEvent: NativeMouseEvent;
        pageX: number;
        pageY: number;
        relatedTarget: EventTarget;
        screenX: number;
        screenY: number;
        shiftKey: boolean;
    }

复制代码

EventTarget 类型实现源码 node_modules/typescript/lib/lib.dom.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


interface EventTarget {
    addEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions): void;
    dispatchEvent(evt: Event): boolean;
    removeEventListener(type: string, listener?: EventListenerOrEventListenerObject | null, options?: EventListenerOptions | boolean): void;
}

复制代码

通过源码我们可以看到 MouseEvent<T = Element> 继承 SyntheticEvent<T>,并且通过 T 接收一个 DOM 元素的类型, currentTarget 的类型由 EventTarget & T 组成交叉类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

事件处理函数类型

当我们定义事件处理函数时有没有更方便定义其函数类型的方式呢?答案是使用 React 声明文件所提供的 EventHandler 类型别名,通过不同事件的 EventHandler 的类型别名来定义事件处理函数的类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

EventHandler 类型实现源码 node_modules/@types/react/index.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


    type EventHandler<E extends SyntheticEvent<any>> = { bivarianceHack(event: E): void }["bivarianceHack"];
    type ReactEventHandler<T = Element> = EventHandler<SyntheticEvent<T>>;
    type ClipboardEventHandler<T = Element> = EventHandler<ClipboardEvent<T>>;
    type DragEventHandler<T = Element> = EventHandler<DragEvent<T>>;
    type FocusEventHandler<T = Element> = EventHandler<FocusEvent<T>>;
    type FormEventHandler<T = Element> = EventHandler<FormEvent<T>>;
    type ChangeEventHandler<T = Element> = EventHandler<ChangeEvent<T>>;
    type KeyboardEventHandler<T = Element> = EventHandler<KeyboardEvent<T>>;
    type MouseEventHandler<T = Element> = EventHandler<MouseEvent<T>>;
    type TouchEventHandler<T = Element> = EventHandler<TouchEvent<T>>;
    type PointerEventHandler<T = Element> = EventHandler<PointerEvent<T>>;
    type UIEventHandler<T = Element> = EventHandler<UIEvent<T>>;
    type WheelEventHandler<T = Element> = EventHandler<WheelEvent<T>>;
    type AnimationEventHandler<T = Element> = EventHandler<AnimationEvent<T>>;
    type TransitionEventHandler<T = Element> = EventHandler<TransitionEvent<T>>;

复制代码

EventHandler 接收 E ,其代表事件处理函数中 event 对象的类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

bivarianceHack 为事件处理函数的类型定义,函数接收一个 event 对象,并且其类型为接收到的泛型变量 E 的类型, 返回值为 void文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

实例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


interface IProps {
  onClick : MouseEventHandler<HTMLDivElement>,
}

复制代码

Promise 类型

在做异步操作时我们经常使用 async 函数,函数调用时会 return 一个 Promise 对象,可以使用 then 方法添加回调函数。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

Promise<T> 是一个泛型类型,T 泛型变量用于确定使用 then 方法时接收的第一个回调函数(onfulfilled)的参数类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

实例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


interface 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)
  })

复制代码

我们首先声明 IResponse 的泛型接口用于定义 response 的类型,通过 T 泛型变量来确定 result 的类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

然后声明了一个 异步函数 getResponse 并且将函数返回值的类型定义为 Promise<IResponse<number[]>>文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

最后调用 getResponse 方法会返回一个 promise 类型,通过 then 调用,此时 then 方法接收的第一个回调函数的参数 response 的类型为,{ message: string, result: number[], success: boolean}文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

Promise<T> 实现源码 node_modules/typescript/lib/lib.es5.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


interface Promise<T> {
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled The callback to execute when the Promise is resolved.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of which ever callback is executed.
     */
    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
    /**
     * Attaches a callback for only the rejection of the Promise.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of the callback.
     */
    catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}

复制代码

工具泛型使用技巧

typeof

一般我们都是先定义类型,再去赋值使用,但是使用 typeof 我们可以把使用顺序倒过来。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


const options = {
  a: 1
}
type Options = typeof options

复制代码
使用字符串字面量类型限制值为固定的字符串参数

限制 props.color 的值只可以是字符串 redblueyellow文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


interface IProps {
  color: 'red' | 'blue' | 'yellow',
}

复制代码
使用数字字面量类型限制值为固定的数值参数

限制 props.index 的值只可以是数字 012文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


interface IProps {
 index: 0 | 1 | 2,
}

复制代码
使用 Partial 将所有的 props 属性都变为可选值

Partial 实现源码 node_modules/typescript/lib/lib.es5.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


type Partial<T> = { [P in keyof T]?: T[P] };

复制代码

上面代码的意思是 keyof T 拿到 T 所有属性名, 然后 in 进行遍历, 将值赋给 P , 最后 T[P] 取得相应属性的值,中间的 ? 用来进行设置为可选值。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

如果 props 所有的属性值都是可选的我们可以借助 Partial 这样实现。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


import { MouseEvent } from 'react'
import * as React from 'react'
interface IProps {
  color: 'red' | 'blue' | 'yellow',
  onClick (event: MouseEvent<HTMLDivElement>): void,
}
const Button: SFC<Partial<IProps>> = ({onClick, children, color}) => {
  return (
    <div onClick={onClick}>
      { children }
    </div>
  )

复制代码
使用 Required 将所有 props 属性都设为必填项

Required 实现源码 node_modules/typescript/lib/lib.es5.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


type Required<T> = { [P in keyof T]-?: T[P] };

复制代码

看到这里,小伙伴们可能有些疑惑, -? 是做什么的,其实 -? 的功能就是把可选属性的 ? 去掉使该属性变成必选项,对应的还有 +? ,作用与 -? 相反,是把属性变为可选项。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

条件类型

TypeScript2.8引入了条件类型,条件类型可以根据其他类型的特性做出类型的判断。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


T extends U ? X : Y

复制代码

原先文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


interface Id { id: number, /* other fields */ }
interface Name { name: string, /* other fields */ }
declare function createLabel(id: number): Id;
declare function createLabel(name: string): Name;
declare function createLabel(name: string | number): Id | Name;

复制代码

使用条件类型文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


type IdOrName<T extends number | string> = T extends number ? Id : Name;
declare function createLabel<T extends number | string>(idOrName: T): T extends number ? Id : Name;

复制代码
Exclude<T,U>

T 中排除那些可以赋值给 U 的类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

Exclude 实现源码 node_modules/typescript/lib/lib.es5.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


type Exclude<T, U> = T extends U ? never : T;

复制代码

实例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


type T = Exclude<1|2|3|4|5, 3|4>  // T = 1|2|5 

复制代码

此时 T 类型的值只可以为 125 ,当使用其他值是 TS 会进行错误提示。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

Error:(8, 5) TS2322: Type '3' is not assignable to type '1 | 2 | 5'.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

Extract<T,U>

T 中提取那些可以赋值给 U 的类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

Extract实现源码 node_modules/typescript/lib/lib.es5.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

type Extract<T, U> = T extends U ? T : never;
复制代码

实例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


type T = Extract<1|2|3|4|5, 3|4>  // T = 3|4

复制代码

此时T类型的值只可以为 34 ,当使用其他值时 TS 会进行错误提示:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

Error:(8, 5) TS2322: Type '5' is not assignable to type '3 | 4'.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

Pick<T,K>

T 中取出一系列 K 的属性。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

Pick 实现源码 node_modules/typescript/lib/lib.es5.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

复制代码

实例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

假如我们现在有一个类型其拥有 nameagesex 属性,当我们想生成一个新的类型只支持 nameage 时可以像下面这样:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


interface Person {
  name: string,
  age: number,
  sex: string,
}
let person: Pick<Person, 'name' | 'age'> = {
  name: '小王',
  age: 21,
}

复制代码
Record<K,T>

K 中所有的属性的值转化为 T 类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

Record 实现源码 node_modules/typescript/lib/lib.es5.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


type Record<K extends keyof any, T> = {
    [P in K]: T;
};

复制代码

实例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

nameage 属性全部设为 string 类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


let person: Record<'name' | 'age', string> = {
  name: '小王',
  age: '12',
}

复制代码
Omit<T,K>(没有内置)

从对象 T 中排除 keyK 的属性。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

由于 TS 中没有内置,所以需要我们使用 PickExclude 进行实现。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


type Omit<T, K> = Pick<T, Exclude<keyof T, K>>

复制代码

实例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

排除 name 属性。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


interface Person {
  name: string,
  age: number,
  sex: string,
}


let person: Omit<Person, 'name'> = {
  age: 1,
  sex: '男'
}

复制代码
NonNullable <T>

排除 Tnullundefined文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

NonNullable 实现源码 node_modules/typescript/lib/lib.es5.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


type NonNullable<T> = T extends null | undefined ? never : T;

复制代码

实例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html


type T = NonNullable<string | string[] | null | undefined>; // string | string[]

复制代码
ReturnType<T>

获取函数 T 返回值的类型。。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

ReturnType 实现源码 node_modules/typescript/lib/lib.es5.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
复制代码

infer R 相当于声明一个变量,接收传入函数的返回值类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

实例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

type T1 = ReturnType<() => string>; // string
type T2 = ReturnType<(s: string) => void>; // void

作者:花生毛豆
来源:掘金文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5909.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/gcs/5909.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定