TS_ React: using generics to improve types

Front end little witch 2022-06-23 18:36:01 阅读数:724

ts_tsreactusinggenerics
*

When we think Impossible is possible When , that The impossible becomes possible , It will really happen -- Pygmalion effect

*

Hello everyone , I am a Seven eight nine .

today , And once again. yòu shuāng ruò zhuó Opened up a new field --TypeScript Practical series .

This is the

  1. JS Basics & principle
  2. JS Algorithm
  3. Front-end engineering
  4. Browser body of knowledge
  5. Css
  6. Network communication
  7. The front frame

These modules , New knowledge system .

This series is mainly aimed at React + TS Of . And about the TS The advantages and benefits of , I'm not going to repeat it , It's been said to suck .

last but not least, This series of articles is TS + React Application articles for , For some basic, such as TS All kinds of data types , Don't make too many introductions . There are many articles on the Internet .

Time will not wait for me , We began to .

What you can learn

*
  1. TypeScript Simple concept
  2. Generic Generics The concept and use of
  3. stay React Use generics to define hook and props
*

Article summary

  1. TypeScript What is it?
  2. Generic Generics What is a
  3. stay React Use generics

1. TypeScript What is it?

*

TypeScript yes ⼀ Species by Microsoft Open source programming language ⾔. It is JavaScript Of ⼀ individual Superset , Essentially to JS Added optional Static type and Class based ⾯ Programming objects .

*

TypeScript Provide up-to-date and evolving JavaScript characteristic , Including those who come ⾃ 2015 Year of ECMAScript And features in future proposals ,⽐ Such as asynchronous function and Decorators, To help build ⽴ Robust components .

About ES and JS Direct relation ,

*

stay In the browser environment ,JS = ECMAScript + DOM + BOM.

*

For more information, please refer to Previous post , We don't distinguish much here ,ES and JS The relationship of .


TypeScript And JavaScript The difference between

TypeScriptJavaScript
JavaScript Of Superset
⽤ To solve ⼤ Type item ⽬ Code complexity
⼀ Kind of Script language ⾔
⽤ Create dynamic ⽹⻚
Can be in During compilation Find and correct mistakes As ⼀ Kind of Explanatory language ⾔, Can only In transit ⾏ I found an error when I did
Strong type ,⽀ Hold static and dynamic types Weak type , There is no static type option
Finally, it was compiled into JavaScript Code , Make the browser understand You can make... Directly in the browser ⽤
⽀ Hold module 、 Generics and join ⼝ No ⽀ Hold generics or join ⼝

obtain TypeScript

command ⾏ Of TypeScript The compiler can make ⽤ npm Package manager to install .

install TypeScript

$ npm install -g typescript

verification TypeScript

$ tsc -v
Version 4.9.x  // TS The latest version

compile TypeScript ⽂ Pieces of

$ tsc helloworld.ts
helloworld.ts => helloworld.js

A typical TypeScript ⼯ Working process

In the picture above, it contains 3 individual ts ⽂ Pieces of :a.tsb.ts and c.ts. these ⽂ Pieces will be TypeScript compiler , Compile to... According to the configured compilation options 3 individual js ⽂ Pieces of , namely a.jsb.js and c.js. about ⼤ Most make ⽤ TypeScript Developed Web term ⽬, We will also compile ⽣ Yes js ⽂ Piece in ⾏ Packaging processing , And then in ⾏ Deploy .

TypeScript Characteristics

TypeScript There are mainly 3 Big feature :

  • Began in JavaScript, belong to JavaScript
    TypeScript Can compile pure 、 concise JavaScript Code , And it can run on any browser 、 Node.js Environment and any support ECMAScript 3( Or later ) Of JavaScript In the engine .
  • Powerful type system
    Type system allow JavaScript Developers are developing JavaScript The application uses efficient development tools and common operations such as static checking and code refactoring .
  • advanced JavaScript
    TypeScript Provide up-to-date and evolving JavaScript characteristic , Including those from 2015 Year of ECMAScript And features in future proposals , For example, asynchronous function and Decorators, To help build robust components .

Generic Generics yes TS An important part of , This article will briefly introduce its concept and introduce it in React Application in .

1. Generic Generics What is a ?

*

Generics are Type parameterization : That is to say, the original specific type is introduced into ⾏ A parameterized

*

Software ⼯ cheng in , We don't just create ⼀ To 、 Well defined API, Also consider Heavyweight ⽤ sex . Components can not only ⽀ Hold the current data type , At the same time ⽀ Hold on to future data types , This is creating ⼤ Type system provides you with ⼗ Flexible functions .

In image C++/Java/Rust Such a tradition OOP language ⾔ in , Sure send ⽤ Generics to create repeatable ⽤ The components of ,⼀ Components can ⽀ Hold multiple types of data . such ⽤ You can With ⾃⼰ The data type of ⽤ Components .

*

Design generic The key ⽬ Of Is in Provide meaningful constraints between members , These members can be : Instance members of a class 、 Class ⽅ Law 、 Function parameters and function return values .

*

for instance , Will be standard TypeScript type And JavaScript object Compare .

//  JavaScript  object 
const user = {
  name: '789',
  status: ' On-line ',
};

// TypeScript  type
type User = {
  name: string;
  status: string;
};

As you can see , They are very similar .

*

The main difference yes

  • stay JavaScript in , It's about variables value
  • stay TypeScript in , It's about variables type
*

About us User type , Its state attribute is too Fuzzy 了 . A state usually has Predefined values , For example, in this example, it can be On-line or offline .

type User = {
  name: string;
  status: ' On-line ' | ' offline ';
};

The above code assumes that we We already know what kind of state there is 了 . If we don't know , The status information may change according to the actual situation ? This requires generics to handle this situation : It allows you to specify a type that can be changed according to usage .

But for us User For example , Use one Generic It looks like this .

// `User`  Now it's a generic type 
const user: User<' On-line ' | ' offline '>;

//  We can manually add a new type  ( Free )
const user: User<' On-line ' | ' offline ' | ' Free '>;

It says user The variable is of type User The object of .

Let's continue to achieve this type .

//  Define a generic type 
type User<StatusOptions> = {
  name: string;
  status: StatusOptions;
};

StatusOptions go by the name of Type variable type variable, and User Said to be The generic type generic type .

In the example above , We used <> To define generics . We can also use functions to define generics .

type User = (StatusOption) => {
  return {
    name: string;
    status: StatusOptions;
  }
}

for example , Imagine our User Accepted a State array , Instead of accepting a single state as before . This is still easy to do with a generic type .

//  Definition type 
type User<StatusOptions> = {
  name: string;
  status: StatusOptions[];
};

// The way types are used remains the same
const user: User<' On-line ' | ' offline '>;

What's the use of generics ?

The above example can define a Status type , Then use it instead of generics .

type Status = ' On-line ' | ' offline ';

type User = {
  name: string;
  status: Status;
};

This is the case in the simpler example , But there are many cases where this cannot be done . The usual situation is , When you want A type is shared across multiple instances , And each instance has some differences : That is, this type is dynamic Of .

⾸ Let's define ⼀ A pass ⽤ Of identity function , Functional Type of return value With its Parameters are the same

function identity (value{
 return value;
}
console.log(identity(1)) // 1

Now? , take identity The function is adjusted appropriately , With ⽀ a TypeScript Of Number Parameters of type :

function identity (value: Number) : Number {
 return value;
}
console.log(identity(1)) // 1

about identity function We will Number Type assigned to Parameters and Return type , Make this function barely enough for ⽤ In this original type . But this function It is not extensible or universal ⽤ Of .

You can put Number Switch to any , In this way, the definition of which type of energy should be returned is lost ⼒, And in the process make The compiler has lost type protection ⽤. our ⽬ Mark is to let identity Function can be applied to ⽤ On Any particular type , To achieve this ⽬ mark , We can make ⽤ Generic To solve this problem , Concrete realization ⽅ The formula is as follows :

function identity <T>(value: T) : T {
 return value;
}
console.log(identity<Number>(1)) // 1

notice <T> grammar , Just Like passing parameters ⼀ sample , The code above conveys what we want ⽤ Specific function calls ⽤ The type of .

Refer to ⾯ Graph ⽚, When we tune ⽤ identity<Number>(1) , Number Types are like parameters 1 ⼀ sample , It will In the presence of T Any position of the type is filled with . In the figure <T> Inside T go by the name of Type variable , It's what we want to pass on to identity Functional Type placeholder , At the same time, it is assigned to value Parameters ⽤ Instead of its type : here T Acting as type ,⽽ It's not specific Number type .

among T representative Type, When defining generics, it is common to ⽤ Make the first ⼀ Type variable names . But actually T Sure ⽤ Any valid name replaces . except T outside , The following are common ⻅ The meaning of generic variables :

  • K(Key): Represents... In an object Key type ;
  • V(Value): Represents... In an object Value type ;
  • E(Element): Express Element type .

It can also lead to ⼊ Want to define Any number of type variables .⽐ If we quote ⼊⼀ A new type variable U ,⽤ To extend our definition of identity function :

function identity <TU>(value: T, message: U) : T {
 console.log(message);
 return value;
}
console.log(identity<Numberstring>(68"TS It really smells good "));

Generic constraint

Sometimes we may wish Limit the number of types each type variable accepts , This is it. Generic constraint Works of ⽤. Next ⾯ Let's take ⼏ Case ⼦, Introduce ⼀ How to make ⽤ Generic constraint .

Make sure the attribute exists

occasionally , We hope There are some attributes on the type corresponding to the type variable . At this time , except ⾮ We explicitly define specific attributes as type variables , Otherwise, the compiler won't know they exist .

For example, when processing strings or arrays , We will assume that length The attribute can be ⽤ Of . Let's make ⽤ identity Function and try to output the ⻓ degree :

function identity<T>(arg: T): T {
 console.log(arg.length); // Error
 return arg;
}

under these circumstances , compiler Will not know T It does contain length attribute , Especially when you can Assign any type to a type variable T Under the circumstances . What we need to do is make the type variable extends ⼀ A connection that contains the attributes we need ⼝,⽐ Like this :

interface Length {
 length: number;
}
function identity<T extends Length>(arg: T): T {
 console.log(arg.length); //  Can get length attribute
 return arg;
}

T extends Length ⽤ Yu tells the compiler , We ⽀ Persistence has been realized Length Pick up ⼝ Any type of .


The arrow function is in jsx Generic syntax in

In the previous example , We only give an example of how generic types can be used to define regular function syntax , instead of ES6 Arrow function syntax introduced in .

// ES6 Arrow function syntax 
const identity = (arg) => {
  return arg;
};

The reason is in use JSX when ,TypeScript The handling of arrow functions is not as good as ordinary functions . According to the above TS Handling functions , Write the following code .

//  It doesn't work 
const identity<ArgType> = (arg: ArgType): ArgType => {
  return arg;
}

//  It doesn't work
const identity = <ArgType>(arg: ArgType): ArgType => {
  return arg;
}

The above two examples , In the use of JSX when , It doesn't work . If you want to handle arrow functions , You need to use the following syntax .

//  The way 1
const identity = <ArgType,>(arg: ArgType): ArgType => {
  return arg;
};

//  The way 2
const identity = <ArgType extends unknown>(arg: ArgType): ArgType => {
  return arg;
};

The root cause of the above problems lies in : This is a TSX(TypeScript+ JSX) Specific syntax for . In the normal TypeScript in , There is no need to use this workaround .


Generic examples :useState

Let's take a look at useState Function type definition of .

function useState<S>(
  initialState: S | (() => S)
): [SDispatch<SetStateAction<S>>]
;

Let's analyze the definition of this type .

  1. First, we define a function ( useState) It accepts a called S Generic variables for
  2. This function takes one and only parameter : initialState( The initial state )
    • This initial state can be of type S( Incoming generics ) The variable of , It can also be a return type of S Function of
  3. useState Returns an array of two elements
    • The first is S type Value ( state value )
    • The second is Dispatch type , The generic parameter is SetStateAction<S>.
      and SetStateAction<S> Itself has received a message of type S Parameters of .

First , Let's see SetStateAction.

type SetStateAction<S> = S | ((prevState: S) => S);

SetStateAction It's also a generic type , The variable it receives can be either a S Variable of type , It can also be a will S Function as its parameter type and return type .

It reminds me that we use setState Definition state when

  • Sure Directly provide new status values ,
  • Or provide a function , Create a new state value from the old state value .

then , Let's go on Dispatch What happened? ?

type Dispatch<A> = (value: A) => void;

Dispatch Is a receive generic parameter A, Functions that do not return any values .

Put them together , Is the following code .

//  The original type 
type Dispatch<SetStateAction<S>>
//  After the merger
type (value: S | ((prevState: S) => S)) => void

It is an acceptance of a value S Or a function S => S, Functions that do not return anything .


3. stay React Use generics

Now that we understand the concept of generics , We can see how to React Apply it to your code .

Use generics to handle Hook

*

Hook It's just ordinary JavaScript function , But in React There is a little extra call timing and rules in . thus it can be seen , stay Hook Use generics on and in common JavaScript Functions are the same .

*
// Ordinary js function 
const greeting = identity<string>('Hello World');

// useState 
const [greeting, setGreeting] = useState<string>('Hello World');

In the example above , You can omit explicit generics , because TypeScript It can be inferred from the parameter value . But sometimes TypeScript You can't do that ( Or do something wrong ), This is the syntax to use .

We are only aiming at useState A class hook Analyze , We also have other hook Make a connection with TS Relevant analysis and processing .

Use generics to handle components props

hypothesis , You are building a for a form select Components . The code is as follows :

Component definition

import { useState, ChangeEvent } from 'react';

function Select({ options }{
  const [value, setValue] = useState(options[0]?.value);

  function handleChange(event: ChangeEvent<HTMLSelectElement>{
    setValue(event.target.value);
  }

  return (
    <select value={value} onChange={handleChange}>
      {options.map((option) => (
        <option key={option.value} value={option.value}>
          {option.label}
        </option>
      ))}
    </select>

  );
}

export default Select;

Component calls


// label  Options
const mockOptions = [
  { value' Banana 'label'' },
  { value' Apple 'label'' },
  { value' Coconut 'label'🥥' },
  { value' watermelon 'label'' },
];

function Form({
  return <Select options={mockOptions} />;
}

hypothesis , about select Options value, We can accept String or number , But you can't accept both .

Let's try the following code .

type Option = {
  value: number | string;
  label: string;
};

type SelectProps = {
  options: Option[];
};

function Select({ options }: SelectProps{
  const [value, setValue] = useState(options[0]?.value);
  ....
  return (
    ....
  );
}

The above code does not satisfy our situation . as a result of , In a select Array , You might have one select The value of is Numeric type , And another one. select The value of is String type . We don't want that , but TypeScript Will accept it .

For example, the following data exists .


const mockOptions = [
  { value123label'' }, //  Numeric type
  { value' Apple 'label'' }, //  String type
  { value' Coconut 'label'🥥' },
  { value' watermelon 'label'' },
];

And we can Use generics to force the component to receive select The value is either a numeric type , Or string type .

type OptionValue = number | string;

//  Generic constraint
type Option<Type extends OptionValue> = {
  value: Type;
  label: string;
};

type SelectProps<Type extends OptionValue> = {
  options: Option<Type>[];
};

Component definition

function Select<Type extends OptionValue>({ options }: SelectProps<Type>{
  const [value, setValue] = useState<Type>(options[0]?.value);

  return (
   ....
  );
}

Why should we define OptionValue , Then add... In many places extends OptionValue.

Imagine , We don't do this , And just use Type extends OptionValue Instead of Type.select How does the component know Type It can be a number or a string , Not the others ?


Postscript

Sharing is an attitude .

Reference material :

The full text after , Now that I see this , If it feels good , Give me a compliment “ Looking at ” Well .

版权声明:本文为[Front end little witch]所创,转载请带上原文链接,感谢。 https://qdmana.com/2022/174/202206231731487396.html