编写第一个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'
错误原因是由于React
和React-dom
并不是使用TS 进行开发的,所以 TS 不知道 React、 React-dom 的类型,以及该模块导出了什么,此时需要引入 .d.ts 的声明文件,比较幸运的是在社区中已经发布了这些常用模块的声明文件 DefinitelyTyped文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html
安装 React
、 React-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 有状态组件,props
、 state
如下文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html
props
props | 类型 | 是否必传 |
---|---|---|
color | string | 是 |
size | string | 否 |
state
state | 类型 |
---|---|
count | string |
使用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泛型类在接收到 P
、S
这两个范型变量后,将只读属性 props
的类型声明为交叉类型 readonly props: Readonly<{ children?: ReactNode }> & Reactonly<P>
使其支持 children
以及我们声明的 color
、size
文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html
通过范型的类型别名 Readonly
将 props
的所有属性都设置为只读属性。文章源自菜鸟学院-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 | 类型 | 是否必传 |
---|---|---|
children | ReactNode | 否 |
onClick | function | 是 |
FC 类型
在React声明文件中,已经定义了一个FC类型,使用这个类型可以避免我们重复定义 propTypes
、contextTypes
、defaultProps
、displayName
的类型。文章源自菜鸟学院-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
事件对象,例如当使用鼠标事件时我们通过 clientX
、clientY
去获取指针的坐标。文章源自菜鸟学院-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
通过 interface
对 event
对象进行类型声明编写的话又十分浪费时间,幸运的是 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
的值只可以是字符串 red
、blue
、yellow
文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html
interface IProps {
color: 'red' | 'blue' | 'yellow',
}
复制代码
使用数字字面量类型限制值为固定的数值参数
限制 props.index
的值只可以是数字 0
、 1
、 2
文章源自菜鸟学院-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
类型的值只可以为 1
、2
、 3
,当使用其他值是 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
类型的值只可以为 3
、4
,当使用其他值时 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
假如我们现在有一个类型其拥有 name
、 age
、 sex
属性,当我们想生成一个新的类型只支持 name
、age
时可以像下面这样:文章源自菜鸟学院-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
将 name
、 age
属性全部设为 string
类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html
let person: Record<'name' | 'age', string> = {
name: '小王',
age: '12',
}
复制代码
Omit<T,K>(没有内置)
从对象 T
中排除 key
是 K
的属性。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/18089.html
由于 TS
中没有内置,所以需要我们使用 Pick
和 Exclude
进行实现。文章源自菜鸟学院-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
排除 T
为 null
、undefined
文章源自菜鸟学院-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