This is the 12th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021
preface
Hello, everyone. Let’s move on to TypeScript. Today I’m going to share with you generics in TypeScript. I don’t know if you’re familiar with generics. Let’s start with a scenario where a function takes only one parameter. The return value type of the function must be the same as the parameter type, and the parameter of the function can accept any type of parameter. So let’s think about how to define this function. Let’s first see if the following code can be implemented
// Use the overloaded functions learned in the previous article
function add(x:number) :number;
function add(x:string) :string;
function add(x:boolean) :boolean;
function add(x:number[]) :number[];
function add(x){return x}
// Use any\ for both parameter type and return value type
function add2(x:any) :any{}
Copy the code
Of the two methods above
- Overloaded functions can satisfy the same type of argument and return value, but not the second requirement: “can accept any type of argument”, that is, can only satisfy the defined parameter type. So this method doesn’t meet the requirements
- The second method has both parameter type and return value type any, which can accept any type of argument, but there is no guarantee that the return and parameter types are the same. So this method doesn’t work either.
This requirement is easy to implement if you know generics. Let’s take a look at what generics are in TypeScript and how to use them.
The generic
- Generic definitions:
Generics are a style and specification of programming languages. When we write code in strongly typed programming languages, we use types that we specify later, specifying them as parameters when we use them. That is, a generic is defined without knowing what type it is, but is used at the developer’s discretion (at the time of use), so that a generic can be defined to support any type. The definition of a variable, as we have seen before, is defined at the time of the definition and cannot be changed. This is where generics are good.
- The use of generics
There are special types of variables in TypeScript called type variables that only represent a type, not a specific value. The general case is represented by a capital letter T. Generics can be used to define functions, interfaces, classes, etc. The syntax for defining generics is simple: the form of the object name < T >. Now that we know about generics, let’s see if the requirement mentioned in the introduction is solved.
function add<T> (arg:T) :T{
return arg
}
// The specific type of T can be specified when the call is made, or the program can be left unspecified to determine the type by type inference
// Use generics correctly
add<number> (1);// Display the specified
add("hello") // Type inference
// Incorrect usage
add<string> (1);// Error: type mismatch
Copy the code
We need to pay attention to when using generic function: the T is representative of a generic type, rather than a specific type, that is to say we have to make this type of parameter is an arbitrary type, and some properties are not free to use, for example, we in the above the body of the function to add such a code:
console.log(arg.length) Copy the code
The compiler will throw an error because T is an indeterminate type and does not necessarily have a length attribute. For example, if we pass a number type, it does not have a length attribute, so an error will be reported.
Generic function types and generic interfaces
In the last article we looked at functions and mentioned that functions can be used as data types of variables, so generic functions can also be used as data types of variables, which we call generic function types. There is little difference in the definition and use of generic function types from ordinary function types. The only difference is that a generic function type needs to be preceded by a type parameter, as follows
let add: <T>(arg:T) = > T;
add = function <T> (arg:T) :T{
return arg;
}
Copy the code
The above code can also be written as an interface, as follows
// Define an interface
interface IAdd{
<T>(arg:T):T
}
let add:IAdd = function <T> (arg:T) :T{
return arg;
}
Copy the code
The above code can also implement a generic function type in the form of an interface, but sometimes there may be more than one member in an interface, and the corresponding type of each member must be the same as the interface type. This leads to generic interfaces.
A generic interface defines a generic parameter directly on the interface as a parameter of the entire interface, so that we can clearly know which generic type is being used, and all members of the interface can also know the parameter type. For example, we continue to modify the above code:
// Define a generic interface
interface IAdd<T>{
(arg:T):T
}
// A generic type of string
let add:IAdd<string> = function <T> (arg:T) :T{
return arg;
}
Copy the code
A generic class
Definition of generic classes: In addition to generic interfaces, we can also define a generic class. The definition of a generic class is similar to that of a generic interface, implementing a generic class definition by putting < T > after the class name.
** The use of generic classes: ** The use of generic classes is the same as ordinary classes, using the keyword new to create an instance of the class, the difference is that a generic class needs to pass a specific type to create the instance, so as to tell us exactly what type is used
Classes can be divided into two parts: the static part and the instance part. A generic class refers to the type of the instance part, while the static part of a class cannot use generic types.
class Add<T>{
result:T;
sum(x:T, y:T):T;
}
let add = new Add<number> (); add.result =0;
add.sum = function(x,y){return x+y}
let hello = new Add<string> (); hello.result ='hello'
hello.sum = function(x,y){return x + y}
Copy the code
Here we do not specify a specific type. Instead, the developer decides what type to use. This can be number, String, Boolean, and so on. That’s where generics come in.
Constraints on generics
After learning about generic interfaces and generic classes, let’s look at generic constraints. In the previous example, an error was reported when we wanted to use the length attribute of a generic type in a generic function. What if we wanted to use the length attribute instead? For example, we declare an interface and define a Length attribute of type Number in the interface. Then we use this interface and the extends keyword to constrain generics. That is, although we define a generic type, the type we pass must conform to the constraint, in this case: the value we pass must contain the length attribute. Look at the following code implementation:
interface ILength{
length:number;
}
function add<T extends ILength> (arg:T) :T{
console.log(arg.length);
}
add(3);// The number 3 does not conform to the generic constraint
add("hello world");// Correct, because the string itself has the length attribute
add({length:20.value:3}); // Objects with length are also generic-compliant
Copy the code
conclusion
In this paper, we get to a new knowledge – generic, based on the study of this paper we understand the definition of a generic as well as the use of the generic type, in fact, this article is just the tip of the iceberg of the generic type, if we use in the daily development of some third party libraries will be the use of various kinds of generics, this article only as generic entry, If you want to know more about generics, you can take a look at other people’s code.
Like small partners welcome to praise the message plus attention oh!