1 the introduction

Dig into the @types/ React source code for some Typescript tips.

2. Intensive reading

Generic extends

Generics can refer to possible parameter types, but the scope of referring to any type is too vague, and the extends modifier can be applied when we need to restrict parameter types or determine that we only deal with certain type parameters.

Problem: React.lazy requires that the return value be a Promise

type, and T must be of the React component type.

Solution:

function lazy<T extends ComponentType<any>>(
  factory: () => Promise<{ default: T }>
): LazyExoticComponent<T>;
Copy the code

T extends ComponentType ensures that T matches the React component definition. Use T in the Promise<{default: T}> position.

The generic extends + infer

If you have a scenario where you need a type that is a subtype within a structure when a parameter conforms to that structure, you need to combine the generic extends + infer.

The first Reducer parameter is the Reducer parameter. The second Reducer parameter is the Reducer parameter. The second Reducer parameter is the Reducer parameter.

Solution:

function useReducer<R extends Reducer<any, any>, I>(
  reducer: R,
  initializerArg: I & ReducerState<R>,
  initializer: (arg: I & ReducerState<R>) => ReducerState<R>
): [ReducerState<R>, Dispatch<ReducerAction<R>>];

type ReducerState<R extends Reducer<any, any>> = R extends Reducer<infer S, any>
  ? S
  : never;
Copy the code

R extends Reducer

, that is, R must conform to the Reducer structure, that is, Reducer must conform to this structure. InitializerArg uses the ReducerState type to scoop up and return the first callback parameter directly from the Reducer’s type R.
,>

ReducerState R extends Reducer

? S: Never: If R meets Reducer

, type S is returned. This type is Reducer

, which is the type of State position. Otherwise, never is returned.


Infer means the type to be inferred. It is a very powerful function that can specify the type to be inferred at any position and extends determines whether the type conforms to the structure.

Another reason to use extends is that extends is the only way to describe structures. Infer can be defined precisely to refer to the location of types.

Type overload

When a type has multiple possibilities, you can define complex types using type overloading, and Typescript will match one by one and find the first one that meets the criteria.

Problem: The first parameter to createElement supports FunctionComponent and ClassComponent, and returns different types of values depending on the parameters passed in.

Solution:

function createElement<P extends {} > (type: FunctionComponent<P>, props? : (Attributes & P) |null. children: ReactNode[] ): FunctionComponentElement<P>;function createElement<P extends {} > (type: ClassType< P, ClassicComponent<P, ComponentState>, ClassicComponentClass<P> >, props? : (ClassAttributes<ClassicComponent<P, ComponentState>> & P) |null. children: ReactNode[] ): CElement<P, ClassicComponent<P, ComponentState>>;Copy the code

Write the createElement double above, with different parameter types and return value types.

The custom type is narrowed

We can do some type narrowing via Typeof or Instanceof, but some types and even custom type narrowing functions need to be customized. We can define custom type narrowing functions via the IS keyword.

Problem: isValidElement determines whether an object is a valid React element. We want this function to narrow the type.

Solution:

function isValidElement<P> (
  object: {} | null | undefined
) :object is ReactElement<P>;

const element: string | ReactElement = "";

if (isValidElement(element)) {
  element; // Automatic deduction type is ReactElement
} else {
  element; // Automatic derivation type is string
}
Copy the code

Based on this scheme, we can create some useful functions, such as isArray, isMap, isSet, etc., that have type narrowing when called through the IS keyword.

Use Interface to define functions

In general, we use type to define function types, but in some cases the function can be called, and there are some default property values that need to be defined, so we can continue to use Interface.

Problem: FunctionComponent can be used as a function call while defining fixed properties such as defaultProps displayName.

Solution:

interfaceFunctionComponent<P = {}> { (props: PropsWithChildren<P>, context? :any): ReactElement<any.any> | null; propTypes? : WeakValidationMap<P>; contextTypes? : ValidationMap<any>; defaultProps? : Partial<P>; displayName? :string;
}
Copy the code

(props: PropsWithChildren

, context? : any) : ReactElement < any, any > | null variables can be used as a function of this type of representation:

const App: FunctionComponent = (a)= > <div />;
App.displayName = "App";
Copy the code

3 summary

After reading this article, you should be able to read all the type definitions of the @types/ React package by yourself!

For more information, read Typescript 2.0-2.9 and Typescript 3.2 New Features. Since TS updates are frequent, you’ll probably continue to read source code for TS tips. I hope you’ll be impressed with the React source code.

The discussion address is: intensive reading of @types/ React Notable TS Tips · Issue #245 · dt-fe/weekly

If you’d like to participate in the discussion, pleaseClick here to, with a new theme every week, released on weekends or Mondays. Front end Intensive Reading – Helps you filter the right content.

Pay attention to the front end of intensive reading wechat public account

Copyright Notice: Freely reproduced – Non-commercial – Non-derivative – Remain signed (Creative Commons 3.0 License)