Generics are an essential concept in computer programming.


Understand generics briefly

What are generics

Generics are a feature of programming languages. Use parameterized types to manipulate multiple data types on the same piece of code.

When it comes to arguments in strongly typed languages, you’re most familiar with defining tangible arguments to function A and then passing the arguments to call A. Specify a variable that represents a type, use it in place of an actual type for programming purposes, and then replace it by passing in the actual type when called, so that a program using generics can actually adapt to different types.

Note: Support for generics varies between programming languages and their compilers and runtime environments

Problems solved by generics

  • reusability
  • Type and algorithm security
  • The efficiency of

This is something that non-generic classes and non-generic methods cannot do

Common situation

You have A function that takes an argument of type A, but when the argument type changes to B, you have to copy the function

For example, the second function in the following code copies the first function — it simply replaces the Integer type with String

func areIntEqual(x: Int, _ y: Int) -> Bool {
  return x == y
}

func areStringsEqual(x: String, _ y: String) -> Bool {
  return x == y
}

areStringsEqual("ray"."ray") / /true
areIntEqual(1, 1) // true
Copy the code

By using generics, you can combine the two functions into one while maintaining type safety. Here is the code implementation

Func areTheyEqual(x: T, _ y: T) -> Bool {func areTheyEqual(x: T, _ y: T) -> Bool {return x == y
}

areTheyEqual("ray"."ray")
areTheyEqual(1, 1)
Copy the code

JavaScript versus generics

Generic and template method (design) patterns

In a series of actions, some are determined, some are not clear, we define the certain behavior in an abstract class, uncertain behavior is defined as an abstract method, by the specific subclass to implementation, this does not affect the whole process, but can deal with all kinds of methods You can call the template method pattern

demo – Coffee or Tea

A few steps:

  • The water to a boil
  • Soak tea leaves in boiling water
  • Pour the tea into the cup
  • With lemon
/* The parent class: Beverage */ var Beverage =function() {}; Beverage.prototype.boilWater =function() {
  console.log("Boil the water.");
};
Beverage.prototype.brew = function() {
  throw new Error("Subclasses must override brew methods");
};
Beverage.prototype.pourInCup = function() {
  throw new Error("Subclasses must override the pourInCup method");
};
Beverage.prototype.addCondiments = function() {
  throw new Error("Subclasses must override the addCondiments method."); }; /* Template method */ beverage.prototype.init =function() { this.boilWater(); this.brew(); this.pourInCup(); this.addCondiments(); } / * -- -- -- -- -- -- -- -- -- -- -- -- line -- -- -- -- -- -- -- -- -- -- -- - * / * / var / * implementation subclass Coffee Coffee =function() {}; Coffee.prototype = new Beverage(); // Override the non-public method coffee.prototype. brew =function() {
  console.log("Brew coffee in boiling water.");
};
Coffee.prototype.pourInCup = function() {
  console.log("Pour the coffee into the cup.");
};
Coffee.prototype.addCondiments = function() {
  console.log("With milk"); }; var coffee = new Coffee(); coffee.init(); /* Implement subclass Tea*/ var Tea =function() {}; Tea.prototype = new Beverage(); // Override the non-public method tea.prototype. brew =function() {
  console.log("Brewing tea with boiling water.");
};
Tea.prototype.pourInCup = function() {
  console.log("Pour the tea into the cup.");
};
Tea.prototype.addCondiments = function() {
  console.log("Add lemon");
};
var tea = new Tea();
tea.init();
Copy the code

Here, the beverage.prototype. init is the so-called template method

It serves as a template for an algorithm that instructs subclasses which methods to execute in which order, and inside it, each step of the algorithm is clearly displayed before our eyes

Generics and TypeScript

  • Generic function
  • A generic class

TypeScript brings strong typing to JavaScriopt, but that means limiting the freedom of types. The same program may need to write different handlers to accommodate different types

And all the logic in these handlers is exactly the same, except for the type — a serious violation of the principles of abstraction and reuse of code

Generic function

Js source

var service = {
    getStringValue: function() {
        return "a string value";
    },
    getNumberValue: function() {
        return20; }};function middleware(value) {
    console.log(value);
    return value;
}

var sValue = middleware(service.getStringValue());
var nValue = middleware(service.getNumberValue());
Copy the code

Ts rewriting uses generics

const service = {
    getStringValue(): string {
        return "a string value";
    },

    getNumberValue(): number {
        return20; }}; // The generic method is modifiedfunction middleware<T>(value: T): T {
    console.log(value);
    return value;
}

var sValue = middleware(service.getStringValue());
var nValue = middleware(service.getNumberValue());
Copy the code

Middleware is followed by declaring a type variable, Value: T indicating that the declared parameter is of type T, and: T indicating that the returned Value is of type T

So far, TS’s modified generic methods are no different from the js code before it was modified. The question now is how can middleware be defined so that it can return both a string and a number, and that it can be correctly inferred by type checking? Code to implement this functionality without using generic methods:

For the first method, use any:

function middleware(value: any): any {
    console.log(value);
    return value;
}
Copy the code

This method can be checked through. The problem with middleware, however, is that it doesn’t have type-checking inside of it, so svalues and nvalues can be assigned as if they’re fine. In short, it’s okay to pretend

Second, multiple middleware:

function middleware1(value: string): string { ... }
function middleware2(value: number): number { ... }
Copy the code

Or use TypeScript overload

function middleware(value: string): string;
function middleware(value: number): number;
functionMiddleware (value: any): any {Copy the code

One of the main problems with this approach is that… If I have 10 types of data and I need to define 10 functions (or overloads), what about 20, 200…

A generic class

That is, declaring a generic type when declaring a class allows the declared generic type to be used throughout the scope of the class, most of the time for container classes

Background: Suppose we need to implement a FilteredList. We can add() any data to it, but it will automatically filter out the unqualified data as we add it. Finally, get all() prints all the eligible data (arrays). Filter conditions are provided as functions or Lambda expressions when constructing objects

Class FilteredList<T> {// Class FilteredList<T> {filter: (v: T) => Boolean; data: T[]; constructor(filter: (v: T) => boolean) { this.filter = filter; } add(value: T) {if (this.filter(value)) {
            this.data.push(value);
        }
    }

    get all(): T[] {
        returnthis.data; }} // Handle string FilteredList const validStrings = new FilteredList< String >(s =>! s); Const positiveNumber = new FilteredList<number>(n => n > 0); // Handle FilteredList const positiveNumber = new FilteredList<number>(n => n > 0);Copy the code

You can even declare (v: T) => Boolean as a type for reuse:

typePredicate<T> = (v: T) => boolean; class FilteredList<T> { filter: Predicate<T>; data: T[]; constructor(filter: Predicate<T>) { ... } add(value: T) { ... } get all(): T[] { ... }}Copy the code

Finally, I hope you can realize your dream as soon as possible!!

Welcome to exchange ~