Typescript: Funktionsüberladung


Wusstet ihr, dass man den Rückgabetyp einer Funktion abhängig von den übergebenen Parametern machen kann?
Relativ einfach kann man dies mit einer sogenannten Funktionsüberladung erreichen.

Ein Beispiel:

Im Beispiel geht es primär um die Datentypen bzw. Funktionssignatur, nicht um die Implementation der Funktion selbst.

Ich möchte eine Funktion appendFooBar schreiben, die an den übergebenen String "FooBar!" anhängt und dann zurück gibt.
Die Funktion soll aber auch einen Array von Strings akzeptieren und "FooBar!" an jeden String des Arrays anhängen und dann den modifizierten Array zurück geben.

Der erste Entwurf sähe so aus:

function appendFooBar(str: string | string[]): string | string[] { 
    if (Array.isArray(str)) {
        return str.map(s => `FooBar! ${s}`);
    } else {
        return `FooBar! ${str}`;
    }
}

Das funktioniert zwar, nervt aber, da die IDE mich zwingt, den unklaren Rückgabetyp entsprechend manuell zu definieren.
Hier also r1 as string bzw. r2 as string[]:

function test() {
    // Aufruf mit einem String
    const r1 = appendFooBar("Hello World!"); 
    console.log((r1 as string).toLocaleLowerCase());

    // Aufruf mit einem Array von Strings
    const r2 = appendFooBar(["Hello World!", "How are you?"]);
    console.log((r2 as string[]).join("; ").toLocaleLowerCase());
}

Viel besser wäre es doch, wenn ich einfach folgendes schreiben könnte und die IDE selbst erkennt ob der Rückgabewert nun ein einfacher String oder ein Array von Strings ist:

function test() {
    const r1 = appendFooBar("Hello World!");
    console.log(r1.toLocaleLowerCase());

    const r2 = appendFooBar(["Hello World!", "How are you?"]);
    console.log(r2.join("; ").toLocaleLowerCase());
}

Dies lässt sich mit Typescript via Funktionsüberladung wie folgt realisieren:

function appendFooBar(str: string): string;
function appendFooBar(str: string[]): string[];
function appendFooBar(str: string | string[]): string | string[] {
    if (Array.isArray(str)) {
        return str.map(s => `FooBar! ${s}`);
    } else {
        return `FooBar! ${str}`;
    }
}

Wichtig ist, dass die Funktionssignatur, die den Funktionskörper definiert, alle möglichen Parameter-Varianten akzeptiert, also:
function appendFooBar(str: string | string[])

Optional können auch alle mögliche Rückgabetypen definiert werden:
function appendFooBar(str: string | string[]): string | string[]

Ein realitätsnahes Beispiel

function getInfo(verbose?: false): { id: number; name: string }
function getInfo(verbose?: true): { id: number; name: string; email: string; roles: string[]; }
function getInfo(verbose?: boolean) {
    if (verbose) {
        return {
            id: 1,
            name: "Hubert",
            email: "hubert@example.com",
            roles: ["author"],
        };
    } else {
        return {
            id: 1,
            name: "Hubert",
        };
    }
}

function useGetInfo() {
    const u1 = getInfo();
    console.log(u1.name);

    const u2 = getInfo(true);
    console.log(u2.email, u2.roles.join(", "));
}

vorheriger Post nächster Post