TypeScript - Generics in TypeScript

in #deutsch2 years ago

Auch in TypeScript gibt es die Möglichkeit mit generischen Datentypen zu arbeiten, genannt Generics. Bedeutet zur Compilierzeit steht nicht fest um welchen Datentyp es sich handelt, erst zur Laufzeit.

Wozu denn Generics?
Nehmen wir an wir möchten die Point2D-Klasse auch für den Datentyp string und boolean haben. Dann müssten wir diese Klasse noch zweimal kopieren und z.B. StringPoint2D und BooleanPoint2D benennen und natürlich die Variablen x und y entsprechend umdeklarieren.
Dadurch hätten wir viel redundanten Code. Außerdem müssten wir bei einer Anpassung, sagen wir mal einer zusätzlichen z-Variable, bei jeder Klasse diese nachträglich angeben.

Unions anstatt Generics?
Würde vielleicht mit zwei oder drei Typen Sinn ergeben. Wir können zwar mit Unions einer Variable sagen, dass sie zur selben Zeit mehrere Typen halten darf, allerdings möchte man nicht sowas stehen haben:

let x : (number|string|boolean|MSServerSocket|PrinterJob|null) = null.

Dann wird es schnell unübersichtlich. Sieht zwar cool aus mit diesen vielen Senkrechtstrichen und runden Klammern aber ich würde es nicht machen.

Dann halt 'any' anstatt Generics
Dann nehmen wir den Datentyp any und machen Generics damit überflüssig, also

let x : any = null

Aber dann ist nicht mehr garantiert, dass die Properties eines Point2D tatsächlich dem gewünschten Datentyp entsprechen.
Eine Nachträgliche überprüfung mit typeof wäre dann die Konsequenz für eine Typsicherheit.

Um Generics verwenden zu können geben wir hinter dem Klassennamen ein T in eckigen Klammern an. Sobald man eine Instanz des Typs Point2D erstellt kann man den gewünschten Datentyp vor der runden Klammer mit angeben. Man kann sie auch wegen der type inference weglassen. Aus Point2D wird somit:

// T is the generic parameter
class Point2D<T> {

    constructor(private x : T, private y : T) {
        this.x = x;
        this.y = y;
    }

    getCoords() : T[] {
        return [this.x,this.y];
    }

    printCoords() : void {
        console.log(`${this.x} ${this.y}`);
    }
}

// now the datatype must be set
let p_number = new Point2D<number>(3,5);
let p_number_coords : number[] = p_number.getCoords();
p_number_coords.forEach(elem => console.log(elem));

let p_boolean = new Point2D<boolean>(true,false);
let p_boolean_coords : boolean[] = p_boolean.getCoords();
p_boolean_coords.forEach(elem => console.log(elem));