Generics i TypeScript

Generics i TypeScript

Denne artikel forklarer generics i TypeScript.

YouTube Video

Generics i TypeScript

Generics i TypeScript er en funktion, der gør det muligt at definere genbrugelige og typesikre funktioner, klasser og interfaces ved at parameterisere typer. Ved at bruge generics kan du skrive kode, der ikke afhænger af specifikke typer, hvilket gør det muligt at udføre de samme operationer på forskellige typer.

Grundlæggende om generics

Generics fungerer som skabeloner, der accepterer typer som argumenter, hvilket gør det muligt for funktioner og klasser at håndtere forskellige typer.

Generiske funktioner

Følgende er et eksempel på en funktion med dens argumenttyper specificeret ved hjælp af generics.

1function identity<T>(value: T): T {
2    return value;
3}
4
5console.log(identity<number>(42));       // 42
6console.log(identity<string>("Hello"));  // Hello
  • T er et generisk typeargument, der repræsenterer typerne for funktionens argumenter og returværdi. Den faktiske type bestemmes, når funktionen kaldes.
  • Ved eksplicit at angive <number> eller <string> specificerer du typen.

Generiske typer fungerer uden eksplicit specificering, fordi TypeScript udfører typeinferens.

1function identity<T>(value: T): T {
2    return value;
3}
4
5console.log(identity(42));       // 42
6console.log(identity("Hello"));  // Hello

Begrænsninger på generics

Ved at sætte begrænsninger på generics kan du begrænse dem til kun at acceptere specifikke typer.

1function loggingIdentity<T extends { length: number }>(arg: T): T {
2    console.log(arg.length);
3    return arg;
4}
5
6loggingIdentity("Hello");  // 5
7loggingIdentity([1, 2, 3]);  // 3
8
9// loggingIdentity(42);  // Error: number does not have a length property.
  • Ved at angive T extends { length: number } indikerer du, at T skal være en type med en length-egenskab. Derfor vil typer uden en length-egenskab ikke blive accepteret.

Generiske klasser

Klasser kan også defineres ved hjælp af generics. Generiske klasser tilbyder fleksible typer til egenskaber og metoder.

 1class Box<T> {
 2    private _value: T;
 3
 4    constructor(value: T) {
 5        this._value = value;
 6    }
 7
 8    public getValue(): T {
 9        return this._value;
10    }
11
12    public setValue(value: T): void {
13        this._value = value;
14    }
15}
16
17const numberBox = new Box<number>(100);
18console.log(numberBox.getValue());  // 100
19
20const stringBox = new Box<string>("Hello");
21console.log(stringBox.getValue());  // Hello
  • Box<T> deklarerer typen T, der bruges inden for klassen som en generisk type. Dette gør det muligt at genbruge den samme klasse til forskellige typer.

Generiske interfaces

Generics kan også bruges sammen med interfaces.

 1interface Pair<T, U> {
 2    first: T;
 3    second: U;
 4}
 5
 6const numberStringPair: Pair<number, string> = { first: 1, second: "One" };
 7console.log(numberStringPair);  // { first: 1, second: 'One' }
 8
 9const booleanArrayPair: Pair<boolean, number[]> = { first: true, second: [1, 2, 3] };
10console.log(booleanArrayPair);  // { first: true, second: [ 1, 2, 3 ] }
  • Ved at angive to generiske typer med Pair<T, U> kan du definere et objekt med en kombination af forskellige typer.

Standard Type Argumenter

Det er også muligt at angive en standardtype for generiske typeargumenter.

1function createArray<T = string>(length: number, value: T): T[] {
2    return Array(length).fill(value);
3}
4
5console.log(createArray(3, "a"));   // ['a', 'a', 'a']
6console.log(createArray(3, 100));   // [100, 100, 100]
  • Vi indstiller standardtypeargumentet til string med <T = string>. Hvis ingen type er angivet eksplicit, vil T være af typen string.

Generiske Typealiasser

Generiske typer kan også bruges som typealiasser (type).

 1type Result<T> = {
 2    success: boolean;
 3    data: T;
 4};
 5
 6const successResult: Result<number> = { success: true, data: 42 };
 7const errorResult: Result<string> = { success: false, data: "Error occurred" };
 8
 9console.log(successResult);  // { success: true, data: 42 }
10console.log(errorResult);    // { success: false, data: 'Error occurred' }
  • Result<T> repræsenterer et resultatobjekt, der indeholder data af typen T. På denne måde kan du oprette fleksible typealiasser ved hjælp af generics.

Flere Generiske Typer

Ved at bruge flere generiske typer kan du definere endnu mere alsidige funktioner og klasser.

1function merge<T, U>(obj1: T, obj2: U): T & U {
2    return { ...obj1, ...obj2 };
3}
4
5const person = { name: "Alice" };
6const job = { title: "Engineer" };
7
8const merged = merge(person, job);
9console.log(merged);  // { name: 'Alice', title: 'Engineer' }
  • merge-funktionen tager to forskellige typer, T og U, og kombinerer dem for at returnere et nyt objekt.

Sammendrag

  • Generics gør det muligt at skrive genbrugelig og typesikker kode ved at behandle typer som parametre.
  • Ved at bruge generics i funktioner, klasser og interfaces kan du skrive fleksibel logik, der håndterer forskellige typer.
  • Ved at tilføje begrænsninger til typeargumenter eller angive standardtypeargumenter, kan du kontrollere, hvordan generics bruges.

Ved hjælp af generics kan du skrive typeuafhængig og generel kode, der udnytter TypeScripts kraftfulde typesystem fuldt ud.

Du kan følge med i ovenstående artikel ved hjælp af Visual Studio Code på vores YouTube-kanal. Husk også at tjekke YouTube-kanalen.

YouTube Video