2016-09-24 4 views
0

Ich habe diese Funktion in Typoskript 2.0:Typoskript 2.0 Kontrollflussanalyse und geben nie

function test(a:string) { 
    var b = typeof a === "function" ? [a] : a; 
} 

Erwartete Verhalten: Typ b ist string. Eine Warnung könnte für eine Bedingung ausgegeben werden, die immer false ist.

Actual Verhalten: Typ b ist never[] | string.

Warum ist das?

+0

Ich weiß nicht, ob es hier eine sehr interessante Antwort gibt, außer dass "tsc" schlau ist, aber nicht * das * schlau. –

+0

Scheint, dass der Compiler intelligent genug ist. Die Rückkehr eines 'Niemals' bedeutet genau das, was Sie erwartet haben. Die einzige Sache, die nicht sehr klar ist, ist, warum es 'nie []' und nicht 'nie' zurückgibt, aber sogar das kann rationalisiert werden, wie Sie erwarten, ein Array zu erhalten, wenn diese Bedingung wahr ist, aber wie es nie der Fall ist bekomme immer noch ein Array zurück. –

Antwort

1

Nur damit gespielt und, darüber nachzudenken, eigentlich ist der TS-Compiler ziemlich schlau. TSC ist sich des Typs des Operators bewusst und verfolgt ihn in der Fließartanalyse.

Einer der wirklich coolen Dinge über TS ist, dass, anstatt eine hohe Stirn, Hohepriester zur Einführung, OO-like (denken Multi-Multi-Level-Klassenhierarchien), kontra und covariant Typen (man denke Scala [+T], [-T] clever aber komplex) auf Sie, TSC versucht einfach, JS-Typen nach dem schönen Durcheinander zu modellieren, das sie sind, und dies geschieht mit der einfachsten Maschinerie, die möglich ist.

Das TS-System ist in gewisser Weise ein Goldlöckchen-System: nicht zu viel und nicht zu wenig; genau richtig.

Also, mit diesem Prolog gehen wir analysieren, was passiert in Antwort auf Ihre Frage "Warum ist das?". Der Typ des ternären Ausdrucks condition ? X : Y ist die Vereinigung von typeof(X) und typeof(Y). Dies liegt daran, dass X im allgemeinen Fall zurückgegeben wird, wenn die Bedingung wahr ist & sein Typ ist typeof(X) und Y wird zurückgegeben & sein Typ ist typeof(Y), so muss es das eine oder andere sein. Jeder JS-Programmierer trägt diese Typflussanalyse in seinem Kopf, weil vanilla JS keine formale Syntax zum Schreiben von Typen hat. Aber TypeScript, FaceBooks Flow & andere Compile-to-JS-Systeme bringen Typensyntax in die JS-Tabelle und so können wir nun den Typ des ternären Konstrukts als typeof(X) | typeof(Y) erkennen.

In Ihrem Beispiel ist X [a] und Y ist string, also der Typ, den der Compiler herauszufinden versucht, ist typeof ([a]) | Art von (a). Die RHS ist einfach: Es ist nur string, weil das Argument der Funktion so sagte. Der LHS-Typ muss ein Array-Typ sein, weil [a] dies sagt und daher in der TS-Syntax als X [] ausgegeben wird. Wenn wir nur herausfinden können, was X ist.

Für den LHS, der Compiler zuerst abgeleitet, dass typeof(a) muss Funktion sein, weil die Bedingung so sagt. Aber die Signatur der Funktion besagt, dass typeof(a)string ist. Daher müssen im Rahmen der [a]typeof(a) gleichzeitig Function sowie string sein. Das hört sich irgendwie quantum an (denke an Quantenzustände), aber es blendet die Wissenschaft nicht wirklich. Es ist eine einfache Konjunktion oder Kreuzung von Typen (große Wörter für den typisierten AND-Operator) und dies kann geschrieben werden als (Funktion & Zeichenfolge). Der gesamte Typ dieses ternären Ausdrucks lautet nun (Function & string)[] | string.

Schließlich weiß der Compiler, dass der Schnittpunkt der Menge aller Funktionswerte und der Menge aller Zeichenfolgenwerte die leere Menge ist.Daher reduziert TSC weiter Function & string zu never, da dieser Wert "nie passieren kann", weshalb TSC in dieser sehr langatmigen Antwort sagt, dass der Typ never[] | string ist.

Es ist jedoch auch wahr, dass eine Reihe von Nevers nie passieren kann, so dass eine andere leere Reihe von Werten ist. Dies bringt uns zu Ihrem erwarteten Verhalten, dass der Typ von b nur string ist, was eine triviale Teilmenge (wie in identischer Menge) zu never[] | string ist. Der Compiler könnte diesen zusätzlichen Schritt berechnet und den Typausdruck weiter reduziert haben. Das ist entweder eine Funktion oder ein Fehler, abhängig davon, ob Sie der Meinung sind, dass Einfachheit ein Feature ist oder nicht!

Inline mit Nitzans Antwort, never[] kann nie passieren so viel wie nie passieren kann, so dass diese Typen die gleiche Menge von Werten darstellen. never[] ist wahrscheinlich ein wenig genauer, weil es Ihnen die Form (es ist ein Container) von dem, was zurückgegeben worden sein könnte, wenn tatsächlich etwas zurückgegeben werden würde.

Auf der anderen Seite wirft Ihre Funktion niemals eine Ausnahme, so dass der Typ auf never | string reduziert werden könnte, wie Nitzan vorschlägt, ist wahrscheinlich falsch oder irreführend. never | string ist der Typ, den Sie für den ternären Ausdruck erhalten wird:

condition ? throw Error("WTF") : "Ecma Scriptus MMXV"

Ich liebe diese kleinen Rätsel und haben oft gefragt, ob ein leerer Korb mit Äpfeln das gleiche wie ein leerer Korb von Orangen ist.

Prost.

Verwandte Themen