TS react: sort the types commonly used in react

  1. 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.
  2. 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
  3. 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>
  1. 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']}>
  1. 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
  1. useState<>() . Strange skills:
const [user,setUser] = React.useState<IUser | null>(null); 
const [user,setUser] = React.useState<IUser>({} as IUser)
  1. 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))
  1. 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)
  1. React. For better reference to state in component, you can use react Component < mystate > and state:MyState {} are annotated
  2. props annotations do not need to be marked ReadOnly. Because ReadOnly is automatically added when it is added to a generic component
  3. class properties
pointer: number
  1. getDerivedStateFromProps
static getDerivedStateFromProps(props:Props, state:State): Partial<State> | null {
}
  1. 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 {
}
  1. 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 />;
};

  1. 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;
};
  1. 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);
  }
}
  1. 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>
  1. 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
  1. 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!
  1. 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;
  1. 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"]
    }
  }
}

Keywords: Javascript Front-end React TypeScript

Added by jpschwartz on Tue, 04 Jan 2022 21:09:16 +0200