TypeScript在React中使用总结

2020-04-0420:28:05WEB前端开发Comments2,609 views字数 13957阅读模式

编写第一个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/18089.html

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

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

安装 ReactReact-dom 类型定义文件

使用yarn安装文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

yarn add @types/react 
yarn add @types/react-dom 
复制代码

使用npm安装文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

npm i @types/react -s
npm i @types/react-dom -s
复制代码

有状态组件开发

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

props

props类型是否必传
colorstring
sizestring

state

state类型
countstring

使用TSX我们可以这样写文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.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 进行解析,充分利用其本身的静态检查功能,使用泛型进行 Props、 State 的类型定义。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

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

P 代表 Props 的类型,S 代表 State 的类型文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

class Component<P, S> {
  readonly props: Readonly<{ children?: ReactNode }> & Reactonly<P>
  state: Reactonly<S>
}
复制代码

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

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

Readonly 实现源码 node_modules/typescript/lib/lib.es5.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.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/18089.html

防止直接更新 state

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

this.state.count = 2
复制代码

我们可以通过将 state,以及 state 下面的属性都设置为只读属性,从而防止直接更新 state文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.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/18089.html

无状态组件开发

Props

props类型是否必传
childrenReactNode
onClickfunction

FC 类型

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

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

   type FC<P = {}> = FunctionComponent<P>;

    interface FunctionComponent<P = {}> {
        (props: PropsWithChildren<P>, context?: any): ReactElement | null;
        propTypes?: WeakValidationMap<P>;
        contextTypes?: ValidationMap<any>;
        defaultProps?: Partial<P>;
        displayName?: string;
    }
复制代码

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

import * as React from 'react'
import { MouseEvent } from 'react'

interface IProps {
  children?: React.ReactNode
  onClick (event: MouseEvent<HTMLDivElement>): void
}

const Button: React.FC<Iprops> = ({onClick, children}) => {
  return (
    <div onClick={onClick}>
      { children }
    </div>
  )
}

export default Button
复制代码

事件处理

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

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

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

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

实例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.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/18089.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/18089.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/18089.html

事件处理函数类型

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

EventHandler 类型实现源码 node_modules/@types/react/index.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.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/18089.html

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

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

interface IProps {
  onClick: MouseEventHandler<HTMLDivElement>
}
复制代码

Promise 类型

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

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

实例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.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/18089.html

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

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

Promise<T> 实现源码 node_modules/typescript/lib/lib.es5.d.ts文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.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>;
}
复制代码

可索引类型

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

interface StringArray { 
 [index: number]: string
}

let myArray: StringArray
myArray = ["Bob", "Fred"]

let myStr: string = myArray[0]
复制代码

上面例子里,我们定义了 StringArray 接口,它具有索引签名。 这个索引签名表示了当用 number 去索引 StringArray 时会得到 string 类型的返回值。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

Typescript 支持两种索引签名:字符串和数字。 可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

是因为当使用 number 来索引时,JavaScript 会将它转换成 string 然后再去索引对象。 也就是说用 100(一个number)去索引等同于使用 "100"(一个string)去索引,因此两者需要保持一致。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

class Animal { 
  name: string
}
class Dog extends Animal {  
  breed: string
}

// 错误:使用数值型的字符串索引,有时会得到完全不同的Animal
interface NotOkay { 
  [x: number]: Animal  
  [x: string]: Dog
}
复制代码

下面的例子里,name 的类型与字符串索引类型不匹配,所以类型检查器给出一个错误提示文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

interface NumberDictionary { 
  [index: string]: number
  length: number    // 可以,length是number类型  
  name: string    // 错误,`name`的类型与索引类型返回值的类型不匹配
}
复制代码

当然,我们也可以将索引签名设置为只读,这样就可以防止给索引赋值文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

interface ReadonlyStringArray { 
  readonly [index: number]: string
}

let myArray: ReadonlyStringArray = ["Alice", "Bob"]
myArray[2] = "Mallory" // error!
复制代码

extends

extends 即为扩展、继承。在 ts 中,extends 关键字既可以来扩展已有的类型,也可以对类型进行条件限定。在扩展已有类型时,不可以进行类型冲突的覆盖操作。例如,A为 string,在扩展出的类型中无法将其改为 number文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

type num = {
  num:number;
}

interface IStrNum extends num {
  str:string;
}

// 与上面等价
type TStrNum = A & {
  str:string;
}
复制代码

ts 中,我们还可以通过条件类型进行一些三目操作:T extends U ? X : Y文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

type IsEqualType<A , B> = A extends B ? (B extends A ? true : false) : false

type NumberEqualsToString = IsEqualType<number,string> // false
type NumberEqualsToNumber = IsEqualType<number,number> // true
复制代码

函数重载

函数重载的基本语法:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

declare function test(a: number): number
declare function test(a: string): string

const resS = test('Hello World')  // resS 被推断出类型为 string
const resN = test(1234)         // resN 被推断出类型为 number
复制代码

这里我们申明了两次?!为什么我不能判断类型或者可选参数呢?后来我遇到这么一个场景:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

interface User {
  name: string
  age: number
}

declare function test(para: User | number, flag?: boolean): number
复制代码

在这个 test 函数里,我们的本意可能是当传入参数 para 是 User 时,不传 flag,当传入 para 是 number 时,传入 flag。TypeScript 并不知道这些,当你传入 para 为 User 时,flag 同样允许你传入:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

const user = {
  name: 'Jack',
  age: 666
}

// 没有报错,但是与想法违背
const res = test(user, false);
复制代码

使用函数重载能帮助我们实现:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

interface User {
  name: string
  age: number
}

declare function test(para: User): number
declare function test(para: number, flag: boolean): number

const user = {
  name: 'Jack',
  age: 666
};

// bingo
// Error: 参数不匹配
const res = test(user, false)
复制代码

实际项目中,你可能要多写几步,如在 class 中:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

interface User {
  name: string
  age: number
}

const user = {
  name: 'Jack',
  age: 123
};

class SomeClass {
  /**
   * 注释 1
   */
  public test(para: User): number
  /**
   * 注释 2
   */
  public test(para: number, flag: boolean): number
  public test(para: User | number, flag?: boolean): number {
    // 具体实现
    return 11
  }
}

const someClass = new SomeClass()

// ok
someClass.test(user)
someClass.test(123, false)

// Error
someClass.test(123)
someClass.test(user, false)
复制代码

函数重载的意义在于能够让你知道传入不同的参数得到不同的结果,如果传入的参数不同,但是得到的结果(类型)却相同,那么这里就不要使用函数重载(没有意义)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

如果函数的返回值类型相同,那么就不需要使用函数重载。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

function func (a: number): number
function func (a: number, b: number): number

// 像这样的是参数个数的区别,我们可以使用可选参数来代替函数重载的定义

function func (a: number, b?: number): number

// 注意第二个参数在类型前边多了一个`?`

// 亦或是一些参数类型的区别导致的
function func (a: number): number
function func (a: string): number

// 这时我们应该使用联合类型来代替函数重载
function func (a: number | string): number
复制代码

工具泛型使用技巧

typeof

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

const options = {
  a: 1
}

type Options = typeof options
复制代码

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

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

interface IProps {
  color: 'red' | 'blue' | 'yellow',
}
复制代码

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

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

interface IProps {
 index: 0 | 1 | 2,
}
复制代码

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

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

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

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

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

import { MouseEvent } from 'react'
import * as React from 'react'

interface IProps {
  children: React.ReactNode
  color: 'red' | 'blue' | 'yellow',
  onClick (event: MouseEvent<HTMLDivElement>): void,
}

const Button: React.FC<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/18089.html

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

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

条件类型

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

T extends U ? X : Y
复制代码

原先文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.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/18089.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/18089.html

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

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

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

type T = Exclude<1|2|3|4|5, 3|4>  // T = 1|2|5 
复制代码

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

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

Extract<T,U>

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

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

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

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

type T = Extract<1|2|3|4|5, 3|4>  // T = 3|4;
复制代码

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

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

Pick<T,K>

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

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

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

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

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

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

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

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

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

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

Omit<T,K>(没有内置)

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

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

type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
复制代码

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

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

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

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

NonNullable

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

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

type NonNullable<T> = T extends null | undefined ? never : T;
复制代码

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

type T = NonNullable<string | string[] | null | undefined>; // string | string[]
复制代码

ReturnType

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

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

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

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

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

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

作者:秃头猿
链接:https://juejin.im/post/5e85911cf265da47c8011de9
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html

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

Comment

匿名网友 填写信息

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

确定