Typescript: Vergleich mit Facebook Flow Type System

Erstellt am 25. Nov. 2014  ·  31Kommentare  ·  Quelle: microsoft/TypeScript

Haftungsausschluss: Dieses Problem hat nicht den Zweck zu beweisen, dass Flow besser oder schlechter als TypeScript ist. Ich möchte nicht die erstaunlichen Arbeiten beider Teams kritisieren, sondern die Unterschiede im Flow- und TypeScript- Typsystem auflisten und versuchen, zu bewerten, welche Funktion vorhanden ist könnte TypeScript verbessern.

Ich werde auch nicht über fehlende Funktionen in Flow sprechen, da der Zweck darin besteht, TypeScript zu verbessern.
Schließlich geht es in diesem Thema nur um das Typsystem und nicht um unterstützte es6 / es7-Funktionen.

mixed und any

Aus dem Flow-Dokument:

  • gemischt: der "Supertyp" aller Typen. Jeder Typ kann in eine Mischung fließen.
  • any: der "dynamische" Typ. Jeder Typ kann in jeden fließen und umgekehrt

Grundsätzlich bedeutet dies, dass mit flow any das Äquivalent von TypeScript any und mixed das Äquivalent von TypeScript {} .

Der Typ Object mit Flow

Aus dem Flow-Dokument:

Verwenden Sie gemischt, um einen Ort zu kommentieren, der alles aufnehmen kann, aber verwenden Sie stattdessen kein Objekt! Es ist verwirrend, alles als Objekt anzusehen, und wenn Sie zufällig "irgendein Objekt" meinen, gibt es eine bessere Möglichkeit, dies anzugeben, genauso wie es eine Möglichkeit gibt, "jede Funktion" anzugeben.

Mit TypeScript entspricht Object {} und akzeptiert jeden Typ, mit Flow Object entspricht {} , unterscheidet sich jedoch von mixed , es werden nur Objekte akzeptiert (und keine anderen primitiven Typen wie string , number , boolean oder function ).

function logObjectKeys(object: Object): void {
  Object.keys(object).forEach(function (key) {
    console.log(key);
  });
}
logObjectKeys({ foo: 'bar' }); // valid with TypeScript and Flow
logObjectKeys(3); // valid with TypeScript, Error with flow

In diesem Beispiel ist der Parameter logObjectKeys mit dem Typ Object , für TypeScript, das {} und akzeptiert daher jeden Typ, wie z. B. number im Fall des zweiten Aufrufs logObjectKeys(3) .
Mit Flow sind andere primitive Typen nicht mit Object kompatibel. Daher meldet der Typprüfer beim zweiten Aufruf logObjectKeys(3) Fehler: _number ist nicht kompatibel mit Object_.

Typ sind nicht null

Aus dem Flow-Dokument:

In JavaScript wird null implizit in alle primitiven Typen konvertiert. Es ist auch ein gültiger Bewohner eines beliebigen Objekttyps.
Im Gegensatz dazu betrachtet Flow null als einen bestimmten Wert, der nicht Teil eines anderen Typs ist.

Siehe Abschnitt Flow doc

Da das Ablaufdokument ziemlich vollständig ist, werde ich diese Funktion nicht im Detail beschreiben. Beachten Sie jedoch, dass der Entwickler gezwungen ist, alle Variablen zu initialisieren oder als nullbar zu markieren. Beispiele:

var test: string; // error undefined is not compatible with `string`
var test: ?string;
function getLength() {
  return test.length // error Property length cannot be initialized possibly null or undefined value
}

Wie bei der TypeScript-Typschutzfunktion sollte Flow jedoch die Nicht-Null-Prüfung verstehen:

var test: ?string;
function getLength() {
  if (test == null) {
    return 0;
  } else {
    return test.length; // no error
  }
}

function getLength2() {
  if (test == null) {
    test = '';
  }
  return test.length; // no error
}

Schnittpunkttyp

Siehe Abschnitt Flow doc
Siehe Correspondin TypeScript-Problem Nr. 1256

Wie TypeScript Flow Support Union-Typen unterstützt es auch eine neue Art der Kombination von Typen: Schnittpunkttypen.
Bei Objekten sind Schnittpunkttypen wie das Deklarieren von Mixins:

type A = { foo: string; };
type B = { bar : string; };
type AB = A & B;

AB hat für Typ { foo: string; bar : string;} ;

Für Funktionen entspricht dies der Deklaration von Überlast:

type A = () => void & (t: string) => void
var func : A;

ist äquivalent zu :

interface A {
  (): void;
  (t: string): void;
}
var func: A

Generische Auflösungserfassung

Betrachten Sie das folgende TypeScript-Beispiel:

declare function promisify<A,B>(func: (a: A) => B):   (a: A) => Promise<B>;
declare function identity<A>(a: A):  A;

var promisifiedIdentity = promisify(identity);

Mit TypeScript hat promisifiedIdentity für den Typ:

(a: {}) => Promise<{}>`.

Mit Flow haben promisifiedIdentity für Typ:

<A>(a: A) => Promise<A>

Typinferenz

Flow versucht im Allgemeinen, mehr Typ als TypeScript abzuleiten.

Rückschluss von Parametern

Schauen wir uns dieses Beispiel an:

function logLength(obj) {
  console.log(obj.length);
}
logLength({length: 'hello'});
logLength([]);
logLength("hey");
logLength(3);

Bei TypeScript werden keine Fehler gemeldet. Bei Flow führt der letzte Aufruf von logLength zu einem Fehler, da number keine length -Eigenschaft hat.

Der abgeleitete Typ ändert sich mit der Verwendung

Bei Flow ändert sich der Typ dieser Variablen mit der Verwendung dieser Variablen, sofern Sie Ihre Variable nicht ausdrücklich eingeben:

var x = "5"; // x is inferred as string
console.log(x.length); // ok x is a string and so has a length property
x = 5; // Inferred type is updated to `number`
x *= 5; // valid since x is now a number

In diesem Beispiel hat x anfangs den Typ string , aber wenn es einer Nummer zugewiesen wurde, wurde der Typ in number geändert.
Mit Typoskript würde die Zuweisung x = 5 zu einem Fehler führen, da x zuvor string zugewiesen wurde und sich sein Typ nicht ändern kann.

Rückschluss auf Unionstypen

Ein weiterer Unterschied besteht darin, dass Flow die Typinferenz rückwärts propagiert, um den abgeleiteten Typ zu einer Typvereinigung zu erweitern. Dieses Beispiel stammt von Facebook / Flow # 67 (Kommentar)

class A { x: string; }
class B extends A { y: number; }
class C extends A { z: number; }

function foo() {
    var a = new B();
    if (true) a = new C(); // TypeScript reports an error, because a's type is already too narrow
    a.x; // Flow reports no error, because a's type is correctly inferred to be B | C
}

("richtig" stammt aus dem Originalbeitrag.)
Da die erfasste Fluss dass der a variable haben könnte B Typen oder C je nach Typ einer bedingten Anweisung wird nun gefolgert B | C , und so die Die Anweisung a.x führt nicht zu einem Fehler, da beide Typen eine x -Eigenschaft haben, wenn wir versucht hätten, auf die z -Eigenschaft zuzugreifen, und ein Fehler aufgetreten wäre.

Dies bedeutet, dass auch Folgendes kompiliert wird.

var x = "5"; // x is inferred as string
if ( true) { x = 5; } // Inferred type is updated to string | number
x.toString(); // Compiles
x += 5; // Compiles. Addition is defined for both string and number after all, although the result is very different

Bearbeiten

  • Der Abschnitt mixed und any aktualisiert, da mixed dem Äquivalent von {} beispielsweise keine Notwendigkeit besteht.
  • Abschnitt für den Typ Object hinzugefügt.
  • Abschnitt zur Typinferenz hinzugefügt

Fühlen Sie sich frei zu benachrichtigen, wenn ich etwas vergessen habe Ich werde versuchen, das Problem zu aktualisieren.

Question

Hilfreichster Kommentar

Persönlich sind generische Erfassung und Nicht-Nullfähigkeit _hochwertige Ziele von Flow. Ich werde den anderen Thread lesen, aber ich wollte auch meine 2c hier reinwerfen.

Ich habe manchmal das Gefühl, dass der Vorteil des Hinzufügens der Nicht-Nullfähigkeit fast jeden Preis wert ist. Es handelt sich um eine Fehlerbedingung mit so hoher Wahrscheinlichkeit, und obwohl die Standardnullbarkeit den integrierten Wert derzeit schwächt, fehlt TypeScript die Möglichkeit, die Nullfähigkeit überhaupt zu diskutieren, indem einfach angenommen wird, dass dies überall der Fall ist.

Ich würde jede Variable, die ich in einem Herzschlag als nicht nullbar finden könnte, mit Anmerkungen versehen.

Alle 31 Kommentare

Dies ist interessant und ein guter Ausgangspunkt für weitere Diskussionen. Stört es Sie, wenn ich aus Gründen der Klarheit einige Änderungen am Originalbeitrag vornehme?

Unerwartete Dinge in Flow (wird diesen Kommentar aktualisieren, wenn ich ihn genauer untersuche)

Inferenz des Argumenttyps für ungerade Funktion:

/** Inference of argument typing doesn't seem
    to continue structurally? **/
function fn1(x) { return x * 4; }
fn1('hi'); // Error, expected
fn1(42); // OK

function fn2(x) { return x.length * 4; }
fn2('hi'); // OK
fn2({length: 3}); // OK
fn2({length: 'foo'}); // No error (??)
fn2(42); // Causes error to be reported at function definition, not call (??)

Keine Typinferenz aus Objektliteralen:

var a = { x: 4, y: 2 };
// No error (??)
if(a.z.w) { }

Dies ist interessant und ein guter Ausgangspunkt für weitere Diskussionen. Stört es Sie, wenn ich aus Gründen der Klarheit einige Änderungen am Originalbeitrag vornehme?

Fühlen Sie sich frei Wie ich bereits sagte, besteht der Zweck darin, ein Flow-Typ-System zu investieren, um festzustellen, ob einige Funktionen in TypeScript 1 passen könnten.

@ RyanCavanaugh Ich denke das letzte Beispiel:

var a = { x: 4, y: 2 };
// No error (??)
if(a.z.w) { }

Ist ein Fehler mit ihrem Null-Check-Algorithmus verbunden, werde ich ihn melden.

Ist

type A = () => void & (t: string) => void
var func : A;

Gleichwertig

Declare A : () => void | (t: string) => void
var func : A;

Oder könnte es sein?

@ Davidhanson90 nicht wirklich:

declare var func: ((t: number) => void) | ((t: string) => void)

func(3); //error
func('hello'); //error

In diesem Beispiel kann flow nicht wissen, welcher Typ im Unionstyp func ist, sodass in beiden Fällen ein Fehler gemeldet wird

declare var func: ((t: number) => void) & ((t: string) => void)

func(3); //no error
func('hello'); //no error

func hat beide Typen, sodass beide Aufrufe gültig sind.

Gibt es einen beobachtbaren Unterschied zwischen {} in TypeScript und mixed in Flow?

@ RyanCavanaugh Ich weiß es nicht wirklich, nachdem ich gedacht habe, dass es ziemlich dasselbe ist,

mixed hat keine Eigenschaften, nicht einmal die Eigenschaften, die von Object.prototype geerbt wurden, die {} hat (# 1108) Dies ist falsch.

Ein weiterer Unterschied besteht darin, dass Flow die Typinferenz rückwärts propagiert, um den abgeleiteten Typ zu einer Typvereinigung zu erweitern. Dieses Beispiel stammt von https://github.com/facebook/flow/issues/67#issuecomment -64221511

class A { x: string; }
class B extends A { y: number; }
class C extends A { z: number; }

function foo() {
    var a = new B();
    if (true) a = new C(); // TypeScript reports an error, because a's type is already too narrow
    a.x; // Flow reports no error, because a's type is correctly inferred to be B | C
}

("richtig" stammt aus dem Originalbeitrag.)

Dies bedeutet, dass auch Folgendes kompiliert wird.

var x = "5"; // x is inferred as string
if ( true) { x = 5; } // Inferred type is updated to string | number
x.toString(); // Compiles
x += 5; // Compiles. Addition is defined for both string and number after all, although the result is very different

Bearbeiten: Das zweite Snippet wurde getestet und es wird wirklich kompiliert.
Bearbeiten 2: Wie von @fdecampredon unten ausgeführt, ist das if (true) { } um die zweite Zuweisung erforderlich, damit Flow den Typ als string | number ableiten kann. Ohne die if (true) wird stattdessen auf number .

Gefällt dir dieses Verhalten? Wir sind diesen Weg gegangen, als wir über Gewerkschaftstypen gesprochen haben, und der Wert ist zweifelhaft. Nur weil das Typsystem jetzt Typen mit mehreren möglichen Zuständen modellieren kann, heißt das nicht, dass es wünschenswert ist, diese überall zu verwenden. Angeblich haben Sie sich für die Verwendung einer Sprache mit einer statischen Typprüfung entschieden, weil Sie Compilerfehler wünschen, wenn Sie Fehler machen, nicht nur, weil Sie gerne Typanmerkungen schreiben;) Das heißt, die meisten Sprachen geben in einem Beispiel wie diesem einen Fehler an (insbesondere die zweite) nicht aus Mangel an einer Möglichkeit, den Typraum zu modellieren, sondern weil sie tatsächlich glauben, dass dies ein Codierungsfehler ist (aus ähnlichen Gründen verzichten viele darauf, viele implizite Cast- / Konvertierungsoperationen zu unterstützen).

Nach der gleichen Logik würde ich dieses Verhalten erwarten:

declare function foo<T>(x:T, y: T): T;
var r = foo(1, "a"); // no problem, T is just string|number

aber ich will dieses Verhalten wirklich nicht.

@danquirk Ich stimme Ihnen zu, dass das automatische Ableiten des Vereinigungstyps anstelle des
Aber ich denke, das kommt von der Flow-Philosophie, mehr als eine echte Sprache. Das Flow-Team versucht, einfach eine Typprüfung zu erstellen. Das ultimative Ziel ist es, "sichereren" Code ohne Typanmerkungen zu erstellen. Dies führte dazu, weniger streng zu sein.

Die genaue Strenge ist angesichts der Auswirkungen dieser Art von Verhalten sogar umstritten. Oft wird nur ein Fehler verschoben (oder vollständig ausgeblendet). Unsere alten Typinferenzregeln für Typargumente spiegelten eine ähnliche Philosophie wider. Im Zweifelsfall haben wir {} für einen Typparameter abgeleitet, anstatt ihn zu einem Fehler zu machen. Dies bedeutete, dass Sie einige doofe Dinge tun und dennoch einige minimale Verhaltensweisen sicher für das Ergebnis ausführen konnten (nämlich Dinge wie toString ). Das Grundprinzip ist, dass einige Leute in JS doofe Dinge tun und wir sollten versuchen zuzulassen, was wir können. In der Praxis waren die meisten Schlussfolgerungen zu {} jedoch nur Fehler, und Sie mussten warten, bis Sie zum ersten Mal eine Variable vom Typ T abgetastet haben, um

declare function foo(arg: number);
var x = "5";
x = 5;
foo(x); // error

Was ist der Fehler hier? Übergibt es wirklich x an foo ? Oder wurde x ein Wert eines völlig anderen Typs zugewiesen, als er initialisiert wurde? Wie oft machen Menschen diese Art der Neuinitialisierung wirklich absichtlich, anstatt versehentlich auf etwas zu stampfen? Wenn Sie auf einen Unionstyp für x können Sie auf jeden Fall wirklich sagen, dass das Typsystem insgesamt weniger streng war, wenn es dennoch zu einem (schlimmeren) Fehler führte? Diese Art der Folgerung ist nur dann weniger streng, wenn Sie mit dem resultierenden Typ, der im Allgemeinen ziemlich selten ist, niemals etwas besonders Bedeutendes tun.

Wenn null und undefined wahrscheinlich auf die gleiche Weise einem Typ ausgeblendet werden können, führt eine Variable, die mit einem Typ eingegeben wurde und einen null verbirgt, meistens zu einem Fehler zur Laufzeit.

Ein nicht unerheblicher Teil des Marketings von Flow basiert auf der Tatsache, dass der Typechecker an Stellen, an denen TS auf any schließen würde, mehr Sinn für Code macht. Die Philosophie lautet, dass Sie keine Anmerkungen hinzufügen müssen, damit der Compiler auf Typen schließen kann. Aus diesem Grund wird das Inferenzrad auf eine viel freizügigere Einstellung eingestellt als das von TypeScript.

Es kommt darauf an, ob jemand die Erwartung hat, dass var x = new B(); x = new C(); (wobei B und C beide von A abgeleitet sind) kompiliert werden sollte oder nicht, und wenn ja, wie sollte daraus geschlossen werden?

  1. Sollte nicht kompilieren.
  2. Sollte kompiliert und als der am meisten abgeleitete Basistyp abgeleitet werden, der den Typhierarchien von B und C - A gemeinsam ist. Für das Zahlen- und Zeichenfolgenbeispiel wäre es {}
  3. Sollte kompiliert und als B | C .

TS macht derzeit (1) und Flow macht (3). Ich bevorzuge (1) und (2) viel mehr als (3).

Ich wollte der Originalausgabe @ Arnavion- Beispiele hinzufügen, aber nachdem ich ein bisschen gespielt hatte, wurde mir klar, dass die Dinge seltsamer waren als das, was wir verstanden haben.
In diesem Beispiel:

var x = "5"; // x is inferred as string
x = 5; // x is infered as number now
x.toString(); // Compiles, since number has a toString method
x += 5; // Compiles since x is a number
console.log(x.length) // error x is a number

Jetzt :

var x = '';
if (true) {
  x = 5;
}

Nach diesem Beispiel ist x string | number
Und wenn ich das tue:

1. var x = ''; 
2. if (true) {
3.  x = 5;
4. }
5. x*=5;

In Zeile 1 wurde die Fehlermeldung angezeigt: myFile.js line 1 string this type is incompatible with myFile.js line 5 number

Ich muss hier noch die Logik herausfinden ...

Es gibt auch einen interessanten Punkt über den Fluss, den ich vergessen habe:

function test(t: Object) { }

test('string'); //error

Grundsätzlich ist 'Objekt' nicht kompatibel mit anderen primitiven Typen, ich denke, dass man Sinn macht.

Die 'Generic Resolution Capture' ist definitiv ein Muss für TS!

@fdecampredon Ja, du hast recht. Mit var x = "5"; x = 5; x wird der abgeleitete Typ auf number aktualisiert. Durch Hinzufügen der if (true) { } um die zweite Zuweisung wird der Typechecker dazu verleitet, anzunehmen, dass eine der beiden Zuweisungen gültig ist, weshalb der abgeleitete Typ stattdessen auf number | string aktualisiert wird.

Der Fehler, den Sie myFile.js line 1 string this type is incompatible with myFile.js line 5 number ist korrekt, da number | string den Operator * nicht unterstützt (die einzigen Operationen, die für einen Unionstyp zulässig sind, sind der Schnittpunkt aller Operationen für alle Arten von Die Union). Um dies zu überprüfen, können Sie es in x += 5 ändern und sehen, dass es kompiliert wird.

Ich habe das Beispiel in meinem Kommentar aktualisiert, um die if (true)

Die 'Generic Resolution Capture' ist definitiv ein Muss für TS!

+1

@Arnavion , nicht sicher, warum Sie {} gegenüber B | C bevorzugen würden. Das Ableiten von B | C erweitert die Anzahl der Programme, die typechecken, ohne die Korrektheit zu beeinträchtigen. Dies ist eine allgemein wünschenswerte Eigenschaft von Typsystemen.

Das Beispiel

declare function foo<T>(x:T, y: T): T;
var r = foo(1, "a"); // no problem, T is just string|number

Bereits unter dem aktuellen Compiler typechecks, außer dass T als {} und nicht als string | number . Dies beeinträchtigt nicht die Korrektheit, ist aber im Großen und Ganzen weniger nützlich.

Die Schlussfolgerung von number | string anstelle von {} erscheint mir nicht problematisch. In diesem speziellen Fall wird die Menge der gültigen Programme nicht erweitert. Wenn die Typen jedoch eine gemeinsame Struktur aufweisen, scheint das Typsystem, das dies erkennt und einige zusätzliche Methoden und / oder Eigenschaften gültig macht, nur eine Verbesserung zu sein.

Das Ableiten von B | C erweitert die Anzahl der Programme, die typchecken, ohne die Korrektheit zu beeinträchtigen

Ich denke, dass das Zulassen der + -Operation für etwas, das entweder eine Zeichenfolge oder eine Zahl sein kann, die Korrektheit beeinträchtigt, da die Operationen überhaupt nicht ähnlich sind. Es ist nicht wie in einer Situation, in der die Operation zu einer gemeinsamen Basisklasse gehört (meine Option 2) - in diesem Fall können Sie eine gewisse Ähnlichkeit erwarten.

Der Operator + wäre nicht aufrufbar, da er zwei inkompatible Überladungen hätte - eine, bei der beide Argumente Zahlen sind, und eine, bei der beide Zeichenfolgen sind. Da B | C ist schmaler als Zeichenfolge und Zahl, es wäre in keiner Überladung als Argument zulässig.

Außer Funktionen sind bivariant in Bezug auf ihre Argumente, so dass dies ein Problem sein könnte?

Ich dachte, da var foo: string; console.log(foo + 5); console.log(foo + document); kompiliert, dass der String + -Operator alles auf der rechten Seite zulässt, hätte string | number + <number> als gültige Operation. Aber du hast recht:

error TS2365: Operator '+' cannot be applied to types 'string | number' and 'number'.

Viele der Kommentare haben sich auf die automatische Erweiterung von Typen in Flow konzentriert. In beiden Fällen können Sie das gewünschte Verhalten erzielen, indem Sie eine Anmerkung hinzufügen. In TS würden Sie bei der Deklaration explizit erweitern: var x: number|string = 5; und in Flow würden Sie bei der Deklaration einschränken: var x: number = 5; . Ich denke, der Fall, für den keine Typdeklaration erforderlich ist, sollte der am häufigsten verwendete sein. In meinen Projekten würde ich erwarten, dass var x = 5; x = 'five'; häufiger ein Fehler ist als ein Unionstyp. Also würde ich sagen, dass TS die Schlussfolgerung in diesem Fall richtig verstanden hat.

Was die Flow-Funktionen betrifft, die meiner Meinung nach am wertvollsten sind?

  1. Nicht-Null-Typen
    Ich denke, dieser hat ein sehr hohes Potenzial, Fehler zu reduzieren. Aus Gründen der Kompatibilität mit vorhandenen TS-Definitionen stelle ich es mir eher als einen Nicht-Null-Modifikator string! als als den nullbaren Modifikator ?string Flow. Ich sehe drei Probleme damit:
    Wie gehe ich mit der Initialisierung von Klassenmitgliedern um? _ (wahrscheinlich müssen sie im ctor zugewiesen werden und wenn sie vor der Zuweisung dem ctor entkommen können, werden sie als nullbar betrachtet) _
    Wie gehe undefined mit
    Kann es ohne viele explizite Typdeklarationen funktionieren?
  2. Unterschied zwischen mixed und Object .
    Denn im Gegensatz zu C # können primitive Typen nicht überall verwendet werden, wo ein Objekt kann. Versuchen Sie Object.keys(3) in Ihrem Browser und Sie erhalten eine Fehlermeldung. Dies ist jedoch nicht kritisch, da ich denke, dass es nur wenige Randfälle gibt.
  3. Generische Auflösungserfassung
    Das Beispiel macht einfach Sinn. Aber ich kann nicht sagen, dass ich viel Code schreibe, der davon profitieren würde. Vielleicht hilft es bei funktionalen Bibliotheken wie Underscore?

Zur automatischen Vereinigungstypinferenz: Ich gehe davon aus, dass "Typinferenz" auf die Typdeklaration beschränkt ist. Ein Mechanismus, der implizit auf eine ausgelassene Typdeklaration folgt. Wie := in Go. Ich bin kein Typtheoretiker, aber nach meinem Verständnis ist Typinferenz ein Compiler-Pass, der jeder impliziten Variablendeklaration (oder jedem Funktionsargument) eine explizite Typanmerkung hinzufügt, die aus dem Typ des Ausdrucks abgeleitet wird, von dem sie zugewiesen wird. Soweit ich weiß, funktioniert dies auf diese Weise für jeden anderen Inferenzmechanismus. C #, Haskell, Go, alle arbeiten so. Oder nicht?

Ich verstehe das Argument, dass JS im wirklichen Leben die TS-Semantik diktieren darf, aber dies ist vielleicht ein guter Punkt, um stattdessen anderen Sprachen zu folgen. Typen sind schließlich der einzige entscheidende Unterschied zwischen JS und TS.

Ich mag viele der Flux-Ideen, aber diese, na ja, wenn es tatsächlich so gemacht wird ... das ist einfach komisch.

Nicht-Null-Typen scheinen für ein modernes Typsystem ein obligatorisches Merkmal zu sein. Wäre es einfach, ts hinzuzufügen?

Weitere Informationen zur Komplexität des Hinzufügens von nicht nullbaren Typen zu TS finden Sie unter https://github.com/Microsoft/TypeScript/issues/185

Es genügt zu sagen, dass die überwiegende Mehrheit der gängigen Sprachen heutzutage so schön wie nicht nullfähige Typen ist, dass sie standardmäßig keine nicht nullbaren Typen haben (wo das Feature wirklich glänzt) oder überhaupt keine allgemeine nicht nullbare Funktion. Und nur wenige, wenn überhaupt, haben versucht, es nachträglich hinzuzufügen (oder erfolgreich hinzuzufügen), aufgrund der Komplexität und der Tatsache, dass so viel vom Wert der Nicht-Nullbarkeit darin liegt, dass es der Standardwert ist (ähnlich wie bei der Unveränderlichkeit). Dies bedeutet nicht, dass wir die Möglichkeiten hier nicht in Betracht ziehen, aber ich würde es auch nicht als obligatorische Funktion bezeichnen.

So sehr ich den Nicht-Null-Typ vermisse, ist die eigentliche Funktion, die ich beim Flow vermisse, die generische Erfassung. Die Tatsache, dass ts jedes Generikum in {} auflöst, macht es wirklich schwierig, es mit einem funktionalen Konstrukt zu verwenden, insbesondere Currying.

Persönlich sind generische Erfassung und Nicht-Nullfähigkeit _hochwertige Ziele von Flow. Ich werde den anderen Thread lesen, aber ich wollte auch meine 2c hier reinwerfen.

Ich habe manchmal das Gefühl, dass der Vorteil des Hinzufügens der Nicht-Nullfähigkeit fast jeden Preis wert ist. Es handelt sich um eine Fehlerbedingung mit so hoher Wahrscheinlichkeit, und obwohl die Standardnullbarkeit den integrierten Wert derzeit schwächt, fehlt TypeScript die Möglichkeit, die Nullfähigkeit überhaupt zu diskutieren, indem einfach angenommen wird, dass dies überall der Fall ist.

Ich würde jede Variable, die ich in einem Herzschlag als nicht nullbar finden könnte, mit Anmerkungen versehen.

Es gibt ziemlich viele versteckte Funktionen in Flow, die auf der Flow-Site nicht dokumentiert sind. Einschließlich SuperType-gebundener und existenzieller Typ

http://sitr.us/2015/05/31/advanced-features-in-flow.html

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

blendsdk picture blendsdk  ·  3Kommentare

weswigham picture weswigham  ·  3Kommentare

kyasbal-1994 picture kyasbal-1994  ·  3Kommentare

bgrieder picture bgrieder  ·  3Kommentare

seanzer picture seanzer  ·  3Kommentare