2016-04-20 16 views
1

Ich habe eine Funktion, die mehrmals aufgerufen wird.Funktion Parameter Typ

function foo(parameter) { 
    parameter.a; 
    parameter.b; 
    parameter.c; 
    parameter.d; 
} 

Die Argumente sind für jeden Aufruf unterschiedlich, aber sie sind alle Literale und haben dieselbe Struktur.

const first = { a: 1, b: 2, c: 3, c: 4}; 
const second = { a: 2, b: 4, c: 6, c: 8}; 

foo(first); 
foo(second); 

Außerdem haben die Objekte eine Menge von Attributen und aus diesem Grund ist es mühsam manuell den Typ des Parameters angeben, die auf meine Frage führt: , wie die Art der foo ‚s schließen Parameter ? Der Compiler leitet die Typen first und second korrekt ab, scheint aber nicht auf den Typ parameter zu schließen.

Hier ist, was ich versucht habe.

  1. Sie können eine Instanz in die Typposition einfügen.

    foo(parameter: typeof first) { 
        parameter.a; 
        parameter.b; 
        //... 
    } 
    

    Es funktioniert und der Typ ist richtig, aber ich möchte mit diesem Ansatz zu vermeiden, da die Funktion und die Literale in verschiedenen Dateien definiert sind, und ich will nicht wörtlich in die Datei importieren mit Funktionsdefinition.

  2. Wenn ich die Funktion in ein Lambda umwandeln, scheint der Compiler den Typ glücklich zu schließen. Es würde wie folgt aussehen:

    function wrapper<T>(parameter: T, foo: (p: T) => void) { 
        foo(parameter); 
    } 
    
    wrapper(first, parameter => { // parameter is inferred to be `typeof first`. 
        parameter.a; 
        parameter.b; 
        //... 
    }) 
    

    Aber sobald ich die labmda in eine Variable extrahieren (was ich brauche, um die Funktion wieder zu verwenden), es wird gefolgert einem.

    function wrapper<T>(parameter: T, foo: (p: T) => void) { 
        foo(parameter); 
    } 
    const lambda = parameter => { // parameter is `any` 
        parameter.a; 
        parameter.b; 
        //... 
    } 
    wrapper(first, lambda) 
    
+0

Ich hatte das gleiche Problem, Interface ist die offensichtliche Lösung, aber ... da wir viele Eigenschaften haben und wir bereits einige Objekt literal geschrieben haben ... und der Compiler abgeleitete den Typ ... können wir diesen abgeleiteten Typ nennen und damit den Typ anderer Objekt- und Funktionsargumente zu deklarieren? –

Antwort

0

Wie wäre es eine Schnittstelle?

interface ILargeObject { 
    a: number; 
    b: number; 
    c: number; 
    d: number; 
} 

const first: ILargeObject = { a: 1, b: 2, c: 3, d: 4 }; 
const second: ILargeObject = { a: 1, b: 2, c: 3, d: 4 }; 

function foo(parameter: ILargeObject) { 
    /* ... */ 
} 

Sie können die Schnittstelle dann in beide Dateien importieren. Dies betrifft Ihre Bedenken, dass ein Literal mit der Funktionsdefinition in die Datei importiert werden muss.

Ein wichtiger Vorteil ist, dass Ihr Objekttyp an einer Stelle definiert wird, anstatt dass der Funktionsparametertyp auf einer einzelnen Objektdefinition beruht. Wenn dieses individuelle Objekt irgendwann in der Zukunft verändert wird, wird es Ihre Funktion damit brechen.

Diese Methode bietet mehr Sicherheit und ist logisch und sauber.

+0

Ich erwähnte in den Fragen, dass "diese Objekte eine Menge von Attributen haben und es deshalb mühsam ist, den Typ des Parameters manuell anzugeben". Außerdem habe ich viele solcher Funktionen und definierende Schnittstellen würden buchstäblich 500 Zeilen Code benötigen. –

+0

Entschuldigung, ich bin mir nicht sicher, wie ich dir helfen kann. Die einzige andere Sache, die ich mir vorstellen kann, ist eine neue Variable für jeden Typ zu definieren (z. B. 'var firstObjType = typeof first' usw.) und diese anstelle der tatsächlichen Objektliterale zu exportieren. Auf diese Weise müssen Sie die Objektliterale nicht in die Datei importieren, die Ihre Funktion (en) hat. –

1

Ich denke, Schnittstellen sind Ihre einzige Option hier. Sie könnten ein Dummy-Objekt mit den Eigenschaften, an denen Sie interessiert sind, deklarieren, aber am Ende des Tages ist das äquivalent.

Wenn Sie diese primitiven Objekte erstellen, erstellt Typescript eine implizite Schnittstelle mit diesen Eigenschaften für diese Variable.

Die Funktion foobenötigt die Typinformationen, die zum Zeitpunkt der Definition erwartet werden, um eine angemessene Typüberprüfung durchführen zu können.

Ihre Ansätze:

  1. Dies funktioniert, weil typeof zuerst die Parameter

    { 
        a:number; 
        b:number; 
        c:number; 
        d:number; 
    } 
    

    die implizit aufgebaut Schnittstelle zu geben und so sind die Felder in der Funktion zur Verfügung.

  2. Dies funktioniert aus dem gleichen Grund, die allgemeine Wrapper-Funktion ist spezialisiert auf den Typ des ersten Parameters (in diesem Fall die first Variable), die dieselbe implizite Schnittstelle ist. Der Parameter wird in diesem Fall als vom selben Typ verstanden und somit sind die Eigenschaften a, b, c, d verfügbar.

  3. Das Lambda wird genau so interpretiert, wie Ihre ursprüngliche Funktionsdefinition interpretiert wurde, ohne Typinformationen für den Parameter. Während dieses Lambda in der Wrapper-Funktion in Ihrem Code verwendet wird, gibt es keine Garantie, dass Lambda nur in einer solchen Weise verwendet wird, so dass es unsicher ist, die Typinformationen zu projizieren. Dies ist auch der Grund, warum Ihre Funktion fehlschlägt.

Jetzt gibt es einige Sprachen, die damit umgehen können. Zum Beispiel denke ich, dass OCaml den Typ einer Datensatzvariablen intelligent implizit anhand der von Ihnen verwendeten Felder und deren Umfang bestimmen kann. Typescript scheint jedoch nicht zu sein. Sie müssen es erzählen, was Sie erwarten. Sie können Inline-Schnittstellen verwenden, wenn Sie möchten, aber wenn Sie angeben, wie lange Sie Ihre Schnittstellen beschreiben, bin ich mir sicher, dass es sich lohnt.

tl; dr: Verwenden Sie explizite Schnittstellen, sie sind gut für Sie.