Er sterkt-typede funksjoner som parametere mulig i TypeScript?


I TypeScript kan jeg erklære en parameter til en funksjon som en type Function. Finnes det en «type-sikker» måte å gjøre dette på som jeg går glipp av? For eksempel, vurder dette:

class Foo {
    save(callback: Function) : void {
        //Do the save
        var result : number = 42; //We get a number from the save operation
        //Can I at compile-time ensure the callback accepts a single parameter of type number somehow?
        callback(result);
    }
}

var foo = new Foo();
var callback = (result: string) : void => {
    alert(result);
}
foo.save(callback);

Lagrings-callbacken er ikke typesikker, jeg gir den en callback-funksjon hvor funksjonens parameter er en streng, men jeg sender et tall, og den kompilerer uten feil. Kan jeg gjøre resultatparameteren i save til en typesikker funksjon?

TL;DR-versjon: finnes det en ekvivalent til en .NET-delegat i TypeScript?

Svar #1

Selvfølgelig. En funksjons type består av typene til argumentet og returtypen. Her spesifiserer vi at typen til callback-parameteren må være "funksjon som aksepterer et tall og returnerer typen any":

class Foo {
    save(callback: (n: number) => any) : void {
        callback(42);
    }
}
var foo = new Foo();

var strCallback = (result: string) : void => {
    alert(result);
}
var numCallback = (result: number) : void => {
    alert(result.toString());
}

foo.save(strCallback); // not OK
foo.save(numCallback); // OK

Hvis du vil, kan du definere et typealias for å kapsle dette inn:

type NumberCallback = (n: number) => any;

class Foo {
    // Equivalent
    save(callback: NumberCallback) : void {
        callback(42);
    }
}

Svar #2

Her er TypeScript-ekvivalenter av noen vanlige .NET-delegater:

interface Action<T>
{
    (item: T): void;
}

interface Func<T,TResult>
{
    (item: T): TResult;
}

Svar #3

type FunctionName = (n: inputType) => any;

class ClassName {
    save(callback: FunctionName) : void {
        callback(data);
    }
}

Dette samsvarer sikkert med paradigmet for funksjonell programmering.

Svar #4

Jeg innser at dette innlegget er gammelt, men det finnes en mer kompakt tilnærming som er litt annerledes enn det som ble spurt om, men som kan være et svært nyttig alternativ. Du kan i praksis erklære funksjonen direkte når du kaller metoden (Foos save() i dette tilfellet). Det vil se omtrent slik ut:

class Foo {
    save(callback: (n: number) => any) : void {
        callback(42)
    }

    multipleCallbacks(firstCallback: (s: string) => void, secondCallback: (b: boolean) => boolean): void {
        firstCallback("hello world")

        let result: boolean = secondCallback(true)
        console.log("Resulting boolean: " + result)
    }
}

var foo = new Foo()

// Single callback example.
// Just like with @RyanCavanaugh's approach, ensure the parameter(s) and return
// types match the declared types above in the `save()` method definition.
foo.save((newNumber: number) => {
    console.log("Some number: " + newNumber)

    // This is optional, since "any" is the declared return type.
    return newNumber
})

// Multiple callbacks example.
// Each call is on a separate line for clarity.
// Note that `firstCallback()` has a void return type, while the second is boolean.
foo.multipleCallbacks(
    (s: string) => {
         console.log("Some string: " + s)
    },
    (b: boolean) => {
        console.log("Some boolean: " + b)
        let result = b && false

        return result
    }
)

Metoden multipleCallback() er veldig nyttig for ting som nettverkskall som kan lykkes eller mislykkes. Igjen, med utgangspunkt i et eksempel med nettverkskall, når multipleCallbacks() blir kalt, kan oppførsel for både suksess og feil defineres på ett sted, noe som gir større klarhet for fremtidige kode-lesere.

Generelt, etter min erfaring, gir denne tilnærmingen seg til å være mer konsis, mindre rotete, og med større klarhet overall.

Lykke til alle!

Relaterte artikler