- React.FC 的注解是有些问题的,在是否优先应用这个类型作为注解上存在一部分争议,因为这个类型毁坏了 JSX.LibraryManagedAttributes,导致其疏忽了函数和类组件的 defaultsProps,displayName 这样的参数, 详见,另外,其不能像 class 组件一样返回 props 的 children 详见(显式的定义 children 属性,或更改源码可解决这个问题)。还有一点,FC 在 @types/react18 之前总是隐式的定义好 children,即便你的 Props 注解并没有定义 children,你依然能够在参数里解构出它。
- 在 @types/react 版本 16.8 和 18 之间能够应用 React.VoidFunctionComponent 或 React.VFC 代替它, 它规定要想在函数体内应用 props 必须显示的定义它
- 因为编译器的限度 在函数组件中,不能返回除 jsx 和 null 以外的值,如果真的须要返回除这两种之外的值,能够应用类型断言,例如
const MyArrayComponent = () => (Array(5).fill(<div/>) as any) as JSX.Element
4.
React.FC<Props> React.Component<Props,state>
- 开发泛型 class 组件
// 在应用的时候束缚它的 props 类型
type SelectProps<T> = {items: T[]};
class Select<T> extends React.Component<SelectProps<T>, {}> {}
// 应用
const Form = () => <Select<string> items={['a','b']}>
- 开发泛型函数
function foo<T>(x:T): T{return x}
// 箭头泛型函数须要用 extends 提醒编辑器这是个泛型
const foo = <T extends Record<string,unknow>>() => {}
7.React.ReactElement 能够通过传入泛型,来注解类或函数组件的实例化后果
class MyAwesomeComponent extends React.Component {render() {return <div>Hello</div>;}
}
const foo: React.ReactElement<MyAwesomeComponent> = <MyAwesomeComponent />; // Okay
const bar: React.ReactElement<MyAwesomeComponent> = <NotMyAwesomeComponent />; // Error
- useState<>() . 奇淫巧技:
const [user,setUser] = React.useState<IUser | null>(null);
const [user,setUser] = React.useState<IUser>({} as IUser)
- reducer 函数的形参里 intialState 的类型注解能够间接 typeOf 它取出它的类型,
action 参数能够应用联结类型活 AnyAction 注解(from’redux‘),
泛型注解
import {Reducer} from 'redux';
export function reducer:
Reducer<AppState, Action>() {}
10.useRef<>(),奇淫巧技:
// 如果能够的话尽量应用的类型具体点
// 比方应用 HTMLDivElement 就比 HTMLElement 好很多,比 Element 好更多
function Foo(){const divRef = useRef<HTMLDivElement>(null);
return <div>etc<div/>
}
11. 类型断言:as/ 泛型注解(React 里不能用)/ 去除 null 或 undefined 断言!
// 非空断言操作符
const fooRef = useRef<string>(null!)
const foo = name!.chartAt(0))
- 函数的执行后果在给其余变量进行赋值,会发现该变量的注解有问题
function fn(){return ['1',false] };
type AType = string[]
let a:AType = fn() // error
// 1. 将其变为 return ['1',false] as any[] 或者 return ['1',false] as string[],如果是只读的能够 as const
// 2. type Atype = (string | boolean)[], 但已不符合实际意义
// 3. react 团队举荐自定义钩子 return 两个以上的值时能够应用对象
13.createContext
type Theme = 'light' | 'dark'
const TeemeContext = React.createContext<Theme>('dark')
// 创立 {} 用断言 const Context = React.createContext({} as ContextState);
const sampleAppContext: TeemeContext = 'light';
export const App = () => (<AppCtx.Provider value={sampleAppContext}>...</AppCtx.Provider>
);
// 如果想创立默认值为 null 或 undefined 能够 React.createContext<string>(undefined!),不然在应用. 时
// 会报没有相干 api 能够调用, 但这样失去了类型平安,或者能够应用一个 helper 函数来帮忙咱们创立,让咱们不再思考 undeined
// 的状况
function createCtx<A extends {} | null>() {const ctx = React.createContext<A | undefined>(undefined);
function useCtx() {const c = React.useContext(ctx);
if (c === undefined)
throw new Error("useCtx must be inside a Provider with a value");
return c;
}
return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple
}
// Usage:
// We still have to specify a type, but no default!
export const [useCurrentUserName, CurrentUserProvider] = createCtx<string>();
function EnthusasticGreeting() {const currentUser = useCurrentUserName();
return <div>HELLO {currentUser.toUpperCase()}!</div>;
}
function App() {
return (
<CurrentUserProvider value="Anders">
<EnthusasticGreeting />
</CurrentUserProvider>
);
}
//
// 整合 useContext,createContext,useState 为一体
export function createCtx<A>(defaultValue: A) {
type UpdateType = React.Dispatch<
React.SetStateAction<typeof defaultValue>
>;
const defaultUpdate: UpdateType = () => defaultValue;
const ctx = React.createContext({
state: defaultValue,
update: defaultUpdate,
});
function Provider(props: React.PropsWithChildren<{}>) {const [state, update] = React.useState(defaultValue);
return <ctx.Provider value={{state, update}} {...props} />;
}
return [ctx, Provider] as const; // alternatively, [typeof ctx, typeof Provider]
}
// usage
const [ctx, TextProvider] = createCtx("someText");
export const TextContext = ctx;
export function App() {
return (
<TextProvider>
<Component />
</TextProvider>
);
}
export function Component() {const { state, update} = React.useContext(TextContext);
return (
<label>
{state}
<input type="text" onChange={(e) => update(e.target.value)} />
</label>
);
}
// 更好的 createContextApi
https://gist.github.com/sw-yx/f18fe6dd4c43fddb3a4971e80114a052
14.useImperativeHandle, forwardRef
export interface MyInputHandles {focus(): void;
}
const MyInput: RefForwardingComponent<MyInputHandles, MyInputProps> = (props, ref) => {const inputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () =>({focus: () => {if(inputRef.current) {inputRef.current.focus();
}
}
}))
return <Input {...props} ref={inputRef}>
}
export default forwardRef(MyInput)
- React.Component 里如果为了更好的援用 state 能够在 React.Component<MyState> 和 state:MyState {}两处做注解
- props 的注解不必标记 readOnly。因为在增加到泛型组件时,会主动增加 ReadOnly
- class properties
pointer: number
- getDerivedStateFromProps
static getDerivedStateFromProps(props:Props, state:State): Partial<State> | null {}
- 当您须要函数的返回值确定状态时能够应用 ReturnType
static getDerivedStateFromProps(props:Props, state:State): Partial<State> | null {}
- ts 中就你就能够不必写 defaultProps 了
21. 如何优雅的取出 component 的 props
const GreetComponent = ({name, age}: {name:string, age:25}) => (<div>{`Hello, my name is ${name}, ${age}`}</div>
);
type ComponentProps<T> = T extends
| React.ComponentType<infer P>
| React.Component<infer P>
? JSX.LibraryManagedAttributes<T, P>
: never;
const TestComponent = (props: ComponentProps<typeof GreetComponent>) => {return <h1 />;};
- react 中常见的 ts 类型
type AppProps = {
message: string;
count: number;
disabled: boolean;
/** array of a type! */
names: string[];
/** string literals to specify exact string values, with a union type to join them together */
status: "waiting" | "success";
/** any object as long as you dont use its properties (NOT COMMON but useful as placeholder) */
obj: object;
obj2: {}; // almost the same as `object`, exactly the same as `Object`
/** an object with any number of properties (PREFERRED) */
obj3: {
id: string;
title: string;
};
/** array of objects! (common) */
objArr: {
id: string;
title: string;
}[];
/** a dict object with any number of properties of the same type */
dict1: {[key: string]: MyTypeHere;
};
dict2: Record<string, MyTypeHere>; // equivalent to dict1
/** any function as long as you don't invoke it (not recommended) */
onSomething: Function;
/** function that doesn't take or return anything (VERY COMMON) */
onClick: () => void;
/** function with named prop (VERY COMMON) */
onChange: (id: number) => void;
/** alternative function type syntax that takes an event (VERY COMMON) */
onClick(event: React.MouseEvent<HTMLButtonElement>): void;
/** an optional prop (VERY COMMON!) */
optional?: OptionalType;
};
- react 中的注解
type AppProps = {
message: string;
count: number;
disabled: boolean;
/** array of a type! */
names: string[];
/** string literals to specify exact string values, with a union type to join them together */
status: "waiting" | "success";
/** any object as long as you dont use its properties (NOT COMMON but useful as placeholder) */
obj: object;
obj2: {}; // almost the same as `object`, exactly the same as `Object`
/** an object with any number of properties (PREFERRED) */
obj3: {
id: string;
title: string;
};
/** array of objects! (common) */
objArr: {
id: string;
title: string;
}[];
/** a dict object with any number of properties of the same type */
dict1: {[key: string]: MyTypeHere;
};
dict2: Record<string, MyTypeHere>; // equivalent to dict1
/** any function as long as you don't invoke it (not recommended) */
onSomething: Function;
/** function that doesn't take or return anything (VERY COMMON) */
onClick: () => void;
/** function with named prop (VERY COMMON) */
onChange: (id: number) => void;
/** alternative function type syntax that takes an event (VERY COMMON) */
onClick(event: React.MouseEvent<HTMLButtonElement>): void;
/** an optional prop (VERY COMMON!) */
optional?: OptionalType;
};
24. 接口与类型别名的差别
类型别名和接口十分类似,在很多状况下你能够自由选择它们。简直所有的性能都在 interface 中可用 type,要害区别在于不能从新关上类型以增加新属性与始终可扩大的接口。
25. 当你想要应用 getDerivedStateFromProps 的返回值作为组建的 state 注解时
// 1. 一般状况
class Comp extends React.Component<
Props,
ReturnType<typeof Comp["getDerivedStateFromProps"]>
> {static getDerivedStateFromProps(props: Props) {}}
// 2. 返回函数
type CustomValue = any;
interface Props {propA: CustomValue;}
interface DefinedState {otherStateField: string;}
type State = DefinedState & ReturnType<typeof transformPropsToState>;
function transformPropsToState(props: Props) {
return {
savedPropA: props.propA, // save for memoization
derivedState: props.propA,
};
}
class Comp extends React.PureComponent<Props, State> {constructor(props: Props) {super(props);
this.state = {
otherStateField: "123",
...transformPropsToState(props),
};
}
static getDerivedStateFromProps(props: Props, state: State) {if (isEqual(props.propA, state.savedPropA)) return null;
return transformPropsToState(props);
}
}
- 表单事件
// 如果不思考性能的话,能够应用内联解决,注解将主动正确生成
const el = (
<button onClick=(e=>{//...})/>
)
// 如果须要在内部定义类型
handlerChange = (e: React.FormEvent<HTMLInputElement>): void => {//}
// 如果在 = 号的右边进行注解
handlerChange: React.ChangeEventHandler<HTMLInputElement> = e => {//}
// 如果在 form 里 onSubmit 的事件,React.SyntheticEvent, 如果有自定义类型,能够应用类型断言
<form
ref={formRef}
onSubmit={(e: React.SyntheticEvent) => {e.preventDefault();
const target = e.target as typeof e.target & {email: { value: string};
password: {value: string};
};
const email = target.email.value; // typechecks!
// etc...
}}
>
<div>
<label>
Email:
<input type="email" name="email" />
</label>
</div>
<div>
<input type="submit" value="Log in" />
</div>
</form>
- 事件类型列表
AnimationEvent:css 动画事件
ChangeEvent:<input>,<select> 和 <textarea> 元素的 change 事件
ClipboardEvent:复制,粘贴,剪切事件
CompositionEvent:因为用户间接输出文本而产生的事件(例如,依据浏览器和 PC 的设置,如果你想在美国键盘上输出日文,可能会呈现一个带有额定字符的弹出窗口)
DragEvent:在设施上拖放和交互的事件
FocusEvent: 元素取得焦点的事件
FormEvent: 当表单元素得失焦点 /value 扭转 / 表单提交的事件
InvalidEvent: 当输出的有效性限度失败时触发(例如 <input type="number" max="10">,有人将插入数字 20)
KeyboardEvent: 键盘键入事件
MouseEvent:鼠标挪动事件
PointerEvent:鼠标、笔 / 触控笔、触摸屏)的交互而产生的事件
TouchEvent:用户与触摸设施交互而产生的事件
TransitionEvent:CSS Transition,浏览器反对度不高
UIEvent:鼠标、触摸和指针事件的根底事件。WheelEvent: 在鼠标滚轮或相似的输出设施上滚动
SyntheticEvent:所有上述事件的根底事件。是否应该在不确定事件类型时应用
// 因为 InputEvent 在各个浏览器反对度不一样,所以能够应用 KeyboardEvent 代替
28.createRef 与 forwardRef
class CssThemeProvider extends React.PureComponent<Props> {private rootRef = React.createRef<HTMLDivElement>(); // like this
render() {return <div ref={this.rootRef}>{this.props.children}</div>;
}
}
// 这样的 forwardRef 是可变的,能够在须要的时候给它赋值
type Props = {children: React.ReactNode; type: "submit" | "button"};
export type Ref = HTMLButtonElement;
export const FancyButton = React.forwardRef<Ref, Props>((props, ref) => (<button ref={ref} className="MyClassName" type={props.type}>
{props.children}
</button>
));
// 如果心愿它不可变
// type Ref = HTMLButtonElement
// (props, ref: React.Ref<Ref>) =>
// 如果你心愿抓取 forwardRef 组件的 props,能够应用 compoentPropsWithRef
29.ReactDOM.createPortal
// Class
const modalRoot = document.getElementById("modal-root") as HTMLElement;
// assuming in your html file has a div with id 'modal-root';
export class Modal extends React.Component {el: HTMLElement = document.createElement("div");
componentDidMount() {modalRoot.appendChild(this.el);
}
componentWillUnmount() {modalRoot.removeChild(this.el);
}
render() {return ReactDOM.createPortal(this.props.children, this.el);
}
}
// hooks
import React, {useEffect, useRef} from "react";
import {createPortal} from "react-dom";
const modalRoot = document.querySelector("#modal-root") as HTMLElement;
const Modal: React.FC<{}> = ({ children}) => {const el = useRef(document.createElement("div"));
useEffect(() => {
// Use this in case CRA throws an error about react-hooks/exhaustive-deps
const current = el.current;
// We assume `modalRoot` exists with '!'
modalRoot!.appendChild(current);
return () => void modalRoot!.removeChild(current);
}, []);
return createPortal(children, el.current);
};
export default Modal;
30. 错误处理
//option1:应用 react-error-boundary
//option2 : 自定义 boundary component
import React, {Component, ErrorInfo, ReactNode} from "react";
interface Props {children: ReactNode;}
interface State {hasError: boolean;}
class ErrorBoundary extends Component<Props, State> {
public state: State = {hasError: false};
public static getDerivedStateFromError(_: Error): State {
// Update state so the next render will show the fallback UI.
return {hasError: true};
}
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {console.error("Uncaught error:", error, errorInfo);
}
public render() {if (this.state.hasError) {return <h1>Sorry.. there was an error</h1>;}
return this.props.children;
}
}
export default ErrorBoundary;
31. 联结类型的弊病,在对象与对象的联结时无奈精准的进行辨别
interface Admin {role: string;}
interface User {email: string;}
// Method 1: use `in` keyword
function redirect(user: Admin | User) {if ("role" in user) {
// use the `in` operator for typeguards since TS 2.7+
routeToAdminPage(user.role);
} else {routeToHomePage(user.email);
}
}
// Method 2: custom type guard, does the same thing in older TS versions or where `in` isnt enough
function isAdmin(user: Admin | User): user is Admin {return (user as any).role !== undefined;
}
// Method ...: typeOf 和 instanceof 也可进行不便的类型爱护
- 期待已久的非空断言整顿用法(最好是理论解决空值,少用此法)
element.parentNode!.removeChild(element); // ! before the period
myFunction(document.getElementById(dialog.id!)!); // ! after the property accessing
let userID!: string; // definite assignment assertion... be careful!
- 用 symbol 创立标识性的 ID 注解
type OrderID = string & {readonly brand: unique symbol};
type UserID = string & {readonly brand: unique symbol};
type ID = OrderID | UserID;
function OrderID(id: string) {return id as OrderID;}
function UserID(id: string) {return id as UserID;}
function queryForUser(id: UserID) {// ...}
queryForUser(OrderID("foobar")); // Error, Argument of type 'OrderID' is not assignable
// to parameter of type 'UserID'
// unique 是一个关键词
// unique T(其中 T 是任何类型)容许在任何地位应用类型,并且名义上用标记标记 T,使来自该地位的 T 只能调配给后果类型。// 它是制作它的每个符号的惟一构造。而后通过交加将其混合到参数类型中,从而生成所有有用的关系
// 译文: 惟一符号的以后行为优先于语法上齐全惟一的符号,然而,如果符号的一个名义子类实际上是须要的,你能够写
// Unique (symbol)来取得一个名义上的品牌符号类型(或 symbol & Unique unknown - 这是完全相同的事件)。惟一
// 符号的行为形式就像锁定在特定申明中的符号,因而在膨胀和控制流时具备非凡能力。名义上的品牌类型在应用上更灵便,// 但不具备弱小的控制流链接的主机申明
// https://github.com/microsoft/TypeScript/pull/33038
// 实例:// type NormalizedPath = unique string;
// type AbsolutePath = unique string;
// type NormalizedAbsolutePath = NormalizedPath & AbsolutePath;
// declare function isNormalizedPath(x: string): x is NormalizedPath;
// declare function isAbsolutePath(x: string): x is AbsolutePath;
// declare function consumeNormalizedAbsolutePath(x: NormalizedAbsolutePath): void;
// const p = "/a/b/c";
// if (isNormalizedPath(p)) {// if (isAbsolutePath(p)) {// consumeNormalizedAbsolutePath(p);
// }
// }
33.Overloading Function
// 1.
function pickCard(x: { suit: string; card: number}[]): number;
function pickCard(x: number): {suit: string; card: number};
function pickCard(x): any {
// implementation with combined signature
// ...
}
// 2.
type pickCard = {(x: { suit: string; card: number}[]): number;
(x: number): {suit: string; card: number};
// no need for combined signature in this form
// you can also type static properties of functions here eg `pickCard.wasCalled`
};
34. 取得组件的 props 类型能够应用 React.ComponentProps<typeof Button>
35.interface 定义的 {a:1,b:2} 和 typeof 这样的对象所取得得类型是不一样的,前者岂但校验 value 的类型还有值
36.js 主动转换 ts 的工具
dts-gen
TypeStat
TypeWiz
js-to-ts-converter
TS-migrate used in Airbnb's conversion
37. 自定义钩子的类型定义标准
declare module 'use-untyped-hook' {export interface InputProps { ...} // type declaration for prop
export interface ReturnProps {...} // type declaration for return props
export default function useUntypedHook(
prop: InputProps
// ...
): ReturnProps;
}
39。为类创立类型定义
import * as React from "react";
declare class SimpleSelect extends React.Component<SimpleSelectProps, any> {}
export interface SimpleSelectProps {
autofocus?: boolean;
cancelKeyboardEventOnSelection?: boolean;
className?: string;
createFromSearch?(items: OptionValue[], search: string): OptionValue;
defaultValue?: OptionValue;
delimiters?: [any];
disabled?: boolean;
// ...
}
export default SimpleSelect;
- 内置类型
ConstructorParameters:类结构函数参数类型的元组
Exclude:将一个类型从另一个类型排除
Extract:抉择可调配给另一类型的子类型
InstanceType:从一个新类构造函数中取得的实例类型
NonNullable:从类型中排除空和未定义
Parameters:函数的形参类型的元组
Partial:使对象中的所有属性都是可选的
Readonly:将对象中的所有属性设置为只读
ReadonlyArray:创立给定类型的不可变数组
Pick:对象类型的一种子类型,蕴含其键的子集
Record:从键类型到值类型的映射
Required:使对象中的所有属性都是必须的
ReturnType:函数的返回类型
41. 如果你在库的官网类型中遇到 bug,你能够将它们复制到本地,并通过“paths”字段通知 TypeScript 应用你的本地版本。在你 tsconfig.json
{
"compilerOptions": {
"paths": {"mobx-react": ["../typings/modules/mobx-react"]
}
}
}