Key Concepts of TS - GENERICS (泛型)

2023-05-17 19:35:06 浏览数 (2)

# Description

代码语言:javascript复制
function identify (arg: number): number {
  return arg;
}

Of course, in TS, we can specify any so that the functioncan be used for any data type:

代码语言:javascript复制
function identify (arg: any): any {
  return arg;
}

We can say that this function is a generalization, since it can work with any type of data. But in this case, we have absolutely no information about the type of the returned value.

When using generalizations, we can explicitly specify the type of the argument and thus, within the framework of our example, determine the type of the return value.

代码语言:javascript复制
function identify<T>(arg: T): T {
  return arg;
}

let numberOutput = identify<number>(5);
let wrongOutput = identify<number>("5"); // Error
let stringOutput = identify<string>("5"); // OK

# Working with Generalized Type Variables

When using generalizations, the compiler assumes that the generalized parameters passed to the function are used correctly. In fact, the compiler treats them as “any data type” or any.

You can describe it as if we are creating an array:

代码语言:javascript复制
function loggingIdentity<T> (arg: T[]): T[] {
  console.log(arg.length); // Array has a .length, so no error
  return arg;
}

This allows us to use the generalized type variable Tas part of the type we are working with, rather than just as an entire type, which gives us more flexibility.

代码语言:javascript复制
function loggingIdentity<T>(arg: Array<T>): Array<T> {
  console.log(arg.length); // Array has a .length, so no error
  return arg;
}

# Generalized Types

Defining a generic function type is very similar to defining a type for a regular function. The only difference is that you first need to specify the type of parameters to be passed and the return value, just like when creating a generalized function:

代码语言:javascript复制
function identity<T>(arg: T): T {
  return arg;
}
let myIdentify: <T>(arg: T) => T = identity;

A different name could be used for the standard parameter, but it is only important that the number of standard parameters and how they are used are consistent.

代码语言:javascript复制
function identify<T>(arg: T): T {
  return arg;
}
let myIdentify: <U>(arg: U) => U = identify;

You can also write a generic type as a call signature on the type of an object literal:

代码语言:javascript复制
function identity<T>(arg: T): T {
  return arg;
}
let myIdentify: {<T>(arg: T): T} = identity;

To the description of the first generalized interface.

代码语言:javascript复制
interface GenericIdentityFn {
  <T>(arg: T): T;
}
function identity<T>(arg: T): T {
  return arg;
}
let myIdentity: GenericIdentityFn = identity;

Moreover, we can specify a generic type for the entire interface, which will make this parameter available to all its methods.

代码语言:javascript复制
interface GenericIdentityFn<T> {
  (arg: T): T;
}
function identity<T>(arg: T): T {
  return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;

In addition to generic interfaces, you can also create generic classes.

Note that you cannot create generic enumerations andnamespaces.

# Generalized Classes

Generalized classes have the same form as generalized interfaces. They have a list of typical parameters in angle brackets (<>) after the class name.

代码语言:javascript复制
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; };

This is a fairly literal use of the GenericNumber type (lit.a generalized number), but you can see that nothing prevents you from using other types with it, except number.

代码语言:javascript复制
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x   y; };
console.log(stringNumeric.add(stringNumeric.zeroValue, "test")); // test

A class has two types: the static part type and the instance type. Generic types are such only in relation to the instance type, but not to the static part type. Therefore, static class members cannot use typical class parameters.

# Limitations of Generalizations

代码语言:javascript复制
function loggingIdentity<T>(arg: T): T {
  console.log(arg.length); // Error! T doesn't have .length property
  return arg;
}

Instead of working with any possible type, we would like to create a constraint so that the function works with all types that have a property .length.

代码语言:javascript复制
interface Lengthwise {
  length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length); // Now we know it has a .length property
  return arg;
}

# Using Generic Parameters in Generalization Constraints

You can define a type parameter that will be limited to the type of another type parameter.

代码语言:javascript复制
function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
console.log(getProperty(x, "a")); // okay
console.log(getProperty(x, "m")); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.

# Using Class Types in Generalizations

When creating object factories in TS using generalizations, you must refer to class types in constructor functions.

代码语言:javascript复制
function create <T>(c: {new(): T; }): T {
  return new c();
}
class SomeClass { }
var obj = create(SomeClass);
console.log(obj.constructor.name); // "SomeClass"

The following example shows how to impose restrictions on the types of classes created using the class factory:

代码语言:javascript复制
class BeeKeeper {
  hasMask: boolean = false;
}
class ZooKeeper {
  nametag: string = "tag";
}
class Animal {
  numLegs: number = 0;
}
class Bee extends Animal {
  keeper: BeeKeeper = new BeeKeeper();
}
class Lion extends Animal {
  keeper: ZooKeeper = new ZooKeeper();
}
function createInstance<TAnimal extends Animal>(c: new () => TAnimal): TAnimal {
  return new c();
}
console.log(createInstance(Lion).keeper.nametag); // tag  
console.log(createInstance(Bee).keeper.hasMask); // false

# new Keyword

To create a new object in the generalization code, we need to specify that the generalized type T has a constructor. This means that instead of the type: T parameter, we need to specify type: {new(): T;}.

代码语言:javascript复制
function UserFactory <T>(): T {
  return new T(); // Error: 'T' only refers to a type, but is being used as a value here.
}

To make the interface work, use the word new:

代码语言:javascript复制
function UserFactory<T> (type: {new(): T;}): T {
  return new type();
}

0 人点赞