- React. There are some problems with FC annotation. There is some controversy on whether to use this type as annotation first, because this type destroys JSX Librarymanagedattributes, which causes it to ignore parameters such as defaultsProps and displayName of function and class components, See details In addition, it cannot return the children of props like the class component See details (this problem can be solved by explicitly defining the children attribute or changing the source code). In addition, FC always implicitly defines children before @ types/react18. Even if your Props annotation does not define children, you can still deconstruct it in the parameters.
- React. Can be used between versions 16.8 and 18 of @ types/react Voidfunctioncomponent or react VFC replaces it by specifying the definition that props must display in order to use it in the function body
- Because the compiler is limited to function components, it cannot return values other than jsx and null. If you really need to return values other than these two, you can use type assertions, such as
const MyArrayComponent = () => (Array(5).fill(<div/>) as any) as JSX.Element
4.
React.FC<Props> React.Component<Props,state>
- Developing generic class components
// Constrain its props type when using type SelectProps<T> = {items: T[]}; class Select<T> extends React.Component<SelectProps<T>, {}> { } // use const Form = () => <Select<string> items={['a','b']}>
- Develop generic functions
function foo<T>(x:T): T{ return x } // The arrow generic function needs to prompt the editor with extensions. This is a generic function const foo = <T extends Record<string,unknow>>() => {}
7.React.ReactElement can annotate the instantiation result of a class or function component by passing in a generic type
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<>() . Strange skills:
const [user,setUser] = React.useState<IUser | null>(null); const [user,setUser] = React.useState<IUser>({} as IUser)
- The type annotation of intialState in the formal parameter of the reducer function can directly get its type from typeOf,
The action parameter can use the union type live AnyAction annotation (from 'redux'),
Generic annotation
import { Reducer } from 'redux'; export function reducer: Reducer<AppState, Action>() {}
10. Useref < > (), special skills:
// If possible, try to use specific types // For example, using HTMLDivElement is much better than HTMLElement and much better than Element function Foo(){ const divRef = useRef<HTMLDivElement>(null); return <div>etc<div/> }
11. Type assertion: as / generic annotation (cannot be used in React) / remove null or undefined assertions!
// Non null assertion operator const fooRef = useRef<string>(null!) const foo = name!.chartAt(0))
- When the execution result of the function assigns values to other variables, it will be found that there is a problem with the annotation of the variable
function fn(){return ['1',false] }; type AType = string[] let a:AType = fn() // error // 1. Change it to return ['1',false] as any [] or return ['1',false] as string []. If it is read-only, it can be as const // 2. type Atype = (string | boolean) [], but it is no longer in line with the actual meaning // 3. The react team recommends that you use the object when you customize the hook return with more than two values
13.createContext
type Theme = 'light' | 'dark' const TeemeContext = React.createContext<Theme>('dark') // Create {} with assertion const context = react createContext({} as ContextState); const sampleAppContext: TeemeContext = 'light'; export const App = () => ( <AppCtx.Provider value={sampleAppContext}>...</AppCtx.Provider> ); // If you want to create a default value of null or undefined, you can react createContext<string>(undefined!), Otherwise, it is in use Time // It will be reported that there is no relevant api to call, but this will lose type safety, or a helper function can be used to help us create it, so that we don't consider undeined // Situation 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> ); } // // Integrate useContext, createContext and 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> ); } //Better 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. For better reference to state in component, you can use react Component < mystate > and state:MyState {} are annotated
- props annotations do not need to be marked ReadOnly. Because ReadOnly is automatically added when it is added to a generic component
- class properties
pointer: number
- getDerivedStateFromProps
static getDerivedStateFromProps(props:Props, state:State): Partial<State> | null { }
- ReturnType can be used when you need the return value of the function to determine the status
static getDerivedStateFromProps(props:Props, state:State): Partial<State> | null { }
- In ts, you don't have to write defaultProps
21. How to extract props of component gracefully
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 />; };
- Common ts types in 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; };
- Comments in 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. Difference between interface and type alias
Type aliases are very similar to interfaces, and in many cases you are free to choose them. Almost all functions are available in the interface. The key difference is that you can't reopen the type to add new attributes and always extensible interfaces.
25. When you want to use the return value of getDerivedStateFromProps as the state annotation of the build
// 1. General situation class Comp extends React.Component< Props, ReturnType<typeof Comp["getDerivedStateFromProps"]> > { static getDerivedStateFromProps(props: Props) {} } // 2. Return function 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); } }
- Form Events
// If performance is not considered, inline processing can be used, and annotations will be generated automatically and correctly const el = ( <button onClick=(e=>{ //... })/> ) // If you need to define a type externally handlerChange = (e: React.FormEvent<HTMLInputElement>): void => { // } // If you annotate to the left of the = sign handlerChange: React.ChangeEventHandler<HTMLInputElement> = e => { // } // If the onSubmit event in the form, react Syntheticevent. If there is a custom type, you can use type assertion <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>
- Event type list
AnimationEvent : css Animation event ChangeEvent: <input>, <select>and<textarea>Elemental change event ClipboardEvent: Copy, paste, cut events CompositionEvent: An event that occurs when a user enters text indirectly(For example, according to the browser and PC If you want to enter Japanese on the American keyboard, a pop-up window with additional characters may appear) DragEvent: Drag and drop and interactive events on the device FocusEvent: Element gets the focus of the event FormEvent: When form elements gain or lose focus/value change/Event for form submission InvalidEvent: Triggered when the validity limit of the input fails(for example<input type="number" max="10">,Someone will insert the number 20) KeyboardEvent: Keyboard typing events MouseEvent: Mouse movement event PointerEvent: Mouse, pen/Stylus, touch screen)Events that occur due to the interaction of TouchEvent: An event that occurs when a user interacts with a touch device TransitionEvent: CSS Transition,Browser support is not high UIEvent: The base event for mouse, touch, and pointer events. WheelEvent: Scroll over the mouse wheel or similar input device SyntheticEvent: The base event of all the above events. Should it be used when the event type is uncertain // Because InputEvent has different support in different browsers, KeyboardEvent can be used instead
28.createRef and forwardRef
class CssThemeProvider extends React.PureComponent<Props> { private rootRef = React.createRef<HTMLDivElement>(); // like this render() { return <div ref={this.rootRef}>{this.props.children}</div>; } } // Such a forwardRef is variable and can be assigned a value when necessary 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> )); // If you want it to be immutable // type Ref = HTMLButtonElement // (props, ref: React.Ref<Ref>) => // If you want to grab props of the forwardRef component, you can use 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. error handling
//Option 1: use react error boundary //option2: Custom 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. The disadvantages of association type are that it is impossible to accurately distinguish between objects in the association of objects
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 and instanceof also provide convenient type protection
- Long awaited non null assertion collation usage (it is better to actually handle null values, and use this method less)
element.parentNode!.removeChild(element); // ! before the period myFunction(document.getElementById(dialog.id!)!); // ! after the property accessing let userID!: string; // definite assignment assertion... be careful!
- Create an identifying ID annotation with symbol
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 is a keyword // Unique T (where t is any type) allows a type to be used anywhere and nominally marks T with a tag so that t from that location can only be assigned to the result type. // It is the unique structure of each symbol that makes it. It is then mixed into parameter types by intersection to generate all useful relationships // The current behavior of unique symbols takes precedence over grammatically completely unique symbols. However, if a nominal subclass of symbols is actually required, you can write // Unique (symbol) to get a nominal brand symbol type (or symbol & unique unknown - this is exactly the same thing). only // Symbols behave like symbols locked in a particular declaration, so they have special capabilities when shrinking and controlling flow. The nominal brand type is more flexible in use, // But there is no host declaration with strong control flow links // https://github.com/microsoft/TypeScript/pull/33038 // example: // 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. To obtain the props type of the component, use react ComponentProps<typeof Button>
35. Objects such as {a:1,b:2} defined by interface and typeof have different types. The former checks not only the type of value, but also the value
36.js tool for automatically converting ts
dts-gen TypeStat TypeWiz js-to-ts-converter TS-migrate used in Airbnb's conversion
37. User defined hook type definition specification
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. Create type definitions for classes
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;
- Built in type
ConstructorParameters : Tuples of class constructor parameter types Exclude: Exclude one type from another Extract: Select a subtype that can be assigned to another type InstanceType: The instance type obtained from a new class constructor NonNullable: Exclude null and undefined from types Parameters: Tuple of parameter type of function Partial: Make all properties in the object optional Readonly: Set all properties in the object to read-only ReadonlyArray: Creates an immutable array of the given type Pick: A subtype of an object type that contains a subset of its keys Record: Mapping from key type to value type Required: Make all attributes in the object necessary ReturnType: Return type of function
41. If you encounter bug s in the official types of the library, you can copy them locally and tell TypeScript to use your local version through the "paths" field. In your tsconfig json
{ "compilerOptions": { "paths": { "mobx-react": ["../typings/modules/mobx-react"] } } }