[TypeScript] TypeScript Learning - Generics

1. Generics

In software engineering, we not only create consistent well-defined APIs, but also take into account 可重用性. Components can support not only current data types, but also future data types, which provides you with great 灵活power when creating large systems.

Without using generics:

function identity(arg: number): number {
    
    
    return arg;
}

In this case, identity can only receive parameters of the number type, and an error will be reported if other parameters are received.
If we use the any type directly:

function identity(arg: any): any {
    
    
    return arg;
}

Using any type will cause this function to receive any type of arg parameter, so some information is lost: the type passed in and the type returned should be the same. If we pass in a number, we 只知道任何类型的值都有可能被返回.

Therefore, we need a way to make 返回值的类型与传入参数的类型the same. Here, we have used 类型变量, which is a special kind of variable that is only used to represent types rather than values.

function identity<T>(arg: T): T {
    
    
    return arg;
}

We added a type variable T to identity. T helps us capture the type passed in by the user (for example: number), and then we can use this type. Then we used T again as the return type. Now we can know that the parameter type is the same as the return value type. This allows us to keep track of information about the types used in the function.

2. Generic method

function identity<T>(arg: T): T {
    
    
    return arg;
}

After we define a generic function, it can be used in two ways. The first is to pass in all parameters, including type parameters:

let output = identity<string>("myString");  // type of output will be 'string'

Here we explicitly specify that T is of string type, and pass it to the function as a parameter, using <> brackets instead of ().
The second method is more general. That is, the compiler will 自动地help us determine the type of T according to the parameters passed in:

let output = identity("myString");  // type of output will be 'string'

3. Generic variables

When using generics to create a generic function like identity, the compiler requires you to use the generic type correctly in the function body. In other words, you have to treat these parameters as if they were 任意或所有类型.

function loggingIdentity<T>(arg: T): T {
    
    
    console.log(arg.length);  // Error: T doesn't have .length
    return arg;
}

If we do this, the compiler will complain that we are using the .length property of arg, but there is nowhere to say that arg has this property. Remember, these type variables represent arbitrary types, so someone using this function may pass in 数字, and numbers do not have a .length property.
Now suppose we want to operate on arrays of type T instead of T directly. Since we are manipulating arrays, the .length property should exist. We can create this array like any other array:

function loggingIdentity<T>(arg: T[]): T[] {
    
    
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}

4. Generic class

Generic classes look similar to generic interfaces. Generic classes use ( < > ) to enclose the generic type, following the class name.

class GenericNumber<T> {
    
    
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) {
    
     return x + y; };

As we said in the class section, a class has two parts: 静态部分and 实例部分. The generic class refers to the type of the instance part, so the static properties of the class cannot use this generic type.

5. Generic constraints

You should remember the previous example, we sometimes want to operate on a set of values ​​of a certain type, and we know what properties this set of values ​​has. In loggingIdentitythe example , we want to access arg的length属性, but the compiler doesn't 不能证明每种类型都有length属性, so there 报错it is.

function loggingIdentity<T>(arg: T): T {
    
    
    console.log(arg.length);  // Error: T doesn't have .length
    return arg;
}

To do this, we define an interface to describe the constraints. Create an interface that includes a .length property, and use this interface with the extends keyword to implement constraints:

interface Lengthwise {
    
    
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    
    
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}

Now the generic function is constrained so it no longer applies to any type:

loggingIdentity(3);  // Error, number doesn't have a .length property

We need to pass in a value that conforms to the constraint type, and must contain the required attributes, that is, the value that contains the length attribute.

Guess you like

Origin blog.csdn.net/ZHANGYANG_1109/article/details/127989505