Generics in TS

Posted by Adam on Fri, 11 Feb 2022 16:57:08 +0100

The most difficult thing in typescript is undoubtedly generics, so what is the motivation for generics
Think about a question: How did generics appear and what problems did it solve
Now there is an echo method, which passes in what type of value and returns what type of value

function echo(arg) {
	return arg
}
const result = echo(123)

The return value of result is an any type, which is not in line with our expectations. Then the types of specified input and output parameters are also not in line with our expectations. In this case, generic types are used

Generics

definition

  • When defining a function, interface or class, you do not specify a specific type in advance, but specify a specific type when using it

use

  • Add a pair of angle brackets after the function name, and the generic name in the angle brackets (habitually written as T, which can be any name),
  • The generic name can be seen as a variable (or as a placeholder)
  • Generics can be any type and can be specified when used
// Insert angle brackets after the function name, write the generic name T, and the parameters and return types are t
function echo<T>(arg:T):T {
	return arg
}
const str: string = 'character string'
// result is of type string
const result = echo(str)
// You can also not specify the type, and ts can automatically infer the type
const result = echo(123) // The result is of type number

Now there is a tuple with two different types of values. Pass this tuple into the swap method, exchange the positions of the two values, and return the tuple after the exchange position

// According to the above requirements, we can realize

// Define tuples
const tuple:[number,string] = [123,'str']

// Implementation of swap method
function swap<T,U>(tuple:[T,U]):[U,T] {
	return [tuple[1],tuple[0]]
}

// call
const result = swap(tuple)

result[0]. Methods that can call strings
result[1]. You can call the method of number

Constrained generics

The following method outputs arg Length there is a wavy line under length, and an error has occurred

The following figure suggests that generic T does not necessarily contain the attribute length, so static type checking will report an error

Then the parameter accepted by this function should be the parameter with the length attribute. Use the extends keyword to restrict the incoming generics
Use extends to make the incoming value meet the constraints

// First, define an IWithLength interface
interface IWithLength {
	length: number
}
// Use the extends keyword after the generic name T to inherit the defined IWithLength interface
function echoWithLength<T extends IWithLength>(arg:T):T {
	console.log(arg.length)
	return arg
}
// Under verification
const str = echoWithLength(`str`)
const obj = echoWithLength({a:1,b:'str'})
const arr = echoWithLength([1,2,3])

Using generics to describe classes

Create a new queue class, implement two methods, enter the queue and leave the queue. At this time, you can use the generic class

// Generic type passed after class name
class Queue <T>{
	private data:T[] = [];
	push(item:T) {
		return this.data.push(item)
	} 
	pop():T {
		this.data.shift()
	}
}
// When instantiating, pass in the desired type
const queue1 = new Queue<number>()
queue1.push(1)
console.log(queue1.pop().toFixed()) // The return type here is number. You can use the number method

// When instantiating, pass in the desired type
const queue2 = new Queue<string>()
queue1.push(`str`)
console.log(queue2.pop().length) // At this time, you can call the method of string

Use generics to describe objects

// Dynamically specify values when defining types 
interface KeyPair<T,U> {
	key: T;
	value: U;
}

let kp1: KeyPair<number,string> = { key: 123, value: 'str' }
let kp2: keypair<string,number> = { key: 'str', value: 123 }

Use generics to describe arrays

Define an array of type number

// Method 1
let arr2: Array<number> = [1,2,3]

// Method 2
interface Info {
	age: number
	name: string
}
let arr1: Info[] = [
	{age: 18, name: 'lili'},
	{age: 10, name: 'hihi'},
]

Using generics to describe functions

// Define a generic interface
interface IPlus<T> {
	(a:T,b:T) : T
}
// Variable binding type
const plus: Iplus<number> = function (a,b){
	return a + b;
}
// number type
const a:Iplus<number> = plus(1,2)
// string type
const b:IPlus<string> = plus('hello','wold')

Topics: Javascript Front-end TypeScript