Preface: This article is a TypeScript series of articles. Designed to use fragmented time to quickly get started with Typescript. Or to revisit old Typescript gaps. On the basis of the official API, add some impressions of daily use. If you are interested ~ welcome to pay attention to the follow-up continue to launch the article.

List of articles:

  • TypeScript basic types
  • Typescript enumeration
  • 3. Typescript interfaces
  • Typescript generics

Directory structure:

  • Why generics
  • Generic constraint
  • Generic syntax
  • The practical application
  • Advanced type: index type
  • React Scenario

Why generics

As a dynamic language, javascript does not know the type of a variable until it is assigned a value. Dynamic languages bring great flexibility to actual coding.

functiongetVal(val) { retrurn val; } getVal(1) // Return the numeric type getVal('1'// Return string type getVal(['2'] // Return the array typeCopy the code

But again, type-related errors can occur during code execution, reducing the maintainability of the code. Now we use typescript to define variables. To support the three invocation methods, we need to declare three type definitions. There are two ways to solve this problem.

  1. Function overloading
function getVal(val: number): number 
function getVal(val: string):string 
function getVal(val: any):any {
    return val;
}
Copy the code
  1. The joint type
function getVal(val: string | number | any[]):string | number | any[] {
    return val;
}
Copy the code

As a programmer, the above two methods make us feel repetitive. This is intolerable. How about a function that can be assigned at run time to determine the type of the variable, with type constraints to reduce errors? The answer is: generics

function getVal<T>(val: T): T {
    return val;
}
Copy the code

T stands for capturing the parameter type passed in by the function and then using T inside the function to declare other variables with that parameter type. But as we can see from the above function, since T is the type of argument that captures the parameter passed in,

This function can take any parameter, which is not consistent with our initial requirement that we only support three types. So let’s introduce the generic constraint.

Generic constraint

type Params=  string | number | any[];
function getVal<T extends Params>(val: T): T {
    return val;
}
getVal(1);
getVal('2');
getVal(['222']);
getVal<number>('3'); GetVal ({}); // Not Param, error reportedCopy the code

Generic syntax

Generics can declare both functions and classes. Interfaces can also be declared

Class Person<T>{} // An Angle bracket follows the class namefunctionPerson<T> {} // an Angle bracket follows the function name. Interface Person<T> {} // An Angle bracket follows the interface nameCopy the code

Sometimes, in a class or a function, he will use more than one dynamic type. But we can only catch one, so why not declare more than one?

function getName<T,U> (name: T, id: U): [T, U] {
    return [name, id]
}
getName('peen', 1);
getName('peen'.'222'); GetName <string, number>('peen'.'22'); / / error:'22'Not numberCopy the code

The practical application

In a real project, where every project would require an interface request, we would encapsulate a generic interface request, and in this function handle some common errors and so on. To make every interface call typescript constrained, remind. Generics are perfectly appropriate here.

interface IApiSourceParams {
    GetList: IGetList
}
interface IGetList {
    id: number;
}
export function fetchApi<T extends keyof IApiSourceParams>(action: T, params: IApiSourceParams[T]) {
    return ajax({
        url: action,
        method: 'POST',
        body: params
    })
}
fetchApi('GetList', { id: 2 });
fetchApi('GetList', { id: '33'}); // Error: id should be numberCopy the code

In this way, we add type constraints to a generic interface request function. Just extend each interface type in IApiSourceParams.

From the above example, you see that T extends Keyof IApiSourceParams, which you didn’t see above. In fact, there are many application scenarios.

Advanced type: index type

Index type query operator: keyof, for any type T, the result of keyof T is the union of known public attribute names on T. It’s a little convoluted, but let’s just look at the example

interface Person {
    name: string;
    age: number;
}
let personProps: keyof Person; // 'name' | 'age'
Copy the code

Index access operator: T[K]. The above keyof is actually the keyof the object, look at the above example

interface IApiSourceParams {
    GetList: IGetList,
    PostApi: IPostApi
}
interface IGetList {
    id: number;
}
export function fetchApi<T extends keyof IApiSourceParams>(action: T, params: IApiSourceParams[T]) {
    return ajax({
        url: action,
        method: 'POST', body: params})} // IApiSourceParams[T] fetch IGetList.Copy the code

React Scenario

React Hook generics are widely used in react libraries

A, the react

If you’ve used React, you’ve probably seen this syntax declare props and state

class Test extends Component<IProps, IState> {}
Copy the code

Take a look at typescript declarations in react Class

class Component<P, S> { static contextType? : Context<any>; context: any; constructor(props: Readonly<P>); constructor(props: P, context? : any);setState<K extends keyof S>( state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null), callback? : () => void ): void; forceUpdate(callBack? : () => void): void; render(): ReactNode;readonlyprops: Readonly<P> & Readonly<{ children? : ReactNode }>; state: Readonly<S>; refs: { [key: string]: ReactInstance }; }Copy the code

You can see react and help make some constraints ahead of time

  1. The props properties of constructor are read-only
  2. SetState can only be the type of K extends Keyof, the declared state.

Second, the React of hooks

Let’s just randomly pick the most commonly used useState method.

function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
Copy the code

So when we use it, we can

const [errorMessage, setError] = useState<string>(' ');

Copy the code

conclusion

This article covers the generic solution scenarios and syntax. Some practical application scenarios are also introduced. Finally, welcome to pay attention to “front-end plus”, seriously learn front-end, do a professional technical people…