TypeScript - Wie gibt man an, dass eine Klassen-Eigenschaft eine ganze Zahl ist?
Ich experimentiere mit TypeScript, und beim Erstellen einer Klasse mit einem ID-Feld, das eine Integer-Zahl sein sollte, bin ich ein wenig verwirrt geworden.
Zunächst einmal sehe ich in Visual Studio 2012 mit dem TypeScript-Plugin int in der IntelliSense-Liste der Typen. Aber ich erhalte einen Kompilierungsfehler, der besagt:
Der Name 'int' existiert nicht im aktuellen Gültigkeitsbereich.
Ich habe die Sprachspezifikationen überprüft und sehe nur die folgenden primitiven Typen: number, string, boolean, null und undefined. Kein integer-Typ.
Also bleiben mir zwei Fragen:
Wie sollte ich Nutzern meiner Klasse anzeigen, dass ein bestimmtes Feld nicht nur eine
Zahl, sondern eineGanzzahlist (und niemals eineFließkomma- oderDezimalzahl)?Warum sehe ich
intin der IntelliSense-Liste, wenn es kein gültiger Typ ist?
Update: Alle Antworten, die ich bisher bekommen habe, drehen sich darum, dass JavaScript keinen Int-Typ hat und es schwierig wäre, einen int-Typ zur Laufzeit durchzusetzen... Ich weiß das alles. Ich frage, ob es einen TypeScript-Weg gibt, um den Benutzern meiner Klasse eine Annotation zu geben, dass dieses Feld eine Integer sein sollte. Vielleicht ein Kommentar in einem bestimmten Format?
Antwort #1
Ich denke, es gibt keinen direkten Weg anzugeben, ob eine Zahl eine Ganzzahl oder eine Gleitkommazahl ist. In Abschnitt 3.2.1 der TypeScript-Spezifikation können wir sehen:
"...Der primitive Typ Number entspricht dem ähnlich benannten primitiven Typ JavaScript und stellt Gleitkommawerte im Doppelpräzisions-64-Bit-Format nach IEEE 754 dar..."
Ich denke,
intist ein Fehler in Visual Studio IntelliSense. Das Richtige istnumber.
Antwort #2
TypeScript ist eine Obermenge von JavaScript, das kein Konzept eines int hat. Es hat nur das Konzept einer Zahl, die eine Gleitkommazahl ist.
Im Allgemeinen könnte die Menge an Arbeit, die der Compiler leisten müsste, um nur ganze Zahlen für einen TypeScript-Int-Typ durchzusetzen, potenziell enorm sein, und in einigen Fällen wäre es dennoch nicht möglich, zur Kompilierzeit sicherzustellen, dass nur ganze Zahlen zugewiesen werden, weshalb es nicht zuverlässig möglich ist, einen int zu TypeScript hinzuzufügen.
Wenn Sie IntelliSense in Visual Studio zum ersten Mal erhalten, ist es für das Tooling nicht möglich zu bestimmen, was geliefert werden soll, also erhalten Sie alles, einschließlich int - aber sobald Sie mit etwas eines bekannten Typs arbeiten, erhalten Sie sinnvolles IntelliSense.
Beispiele
var myInt: number;
var myString: string;
myInt. // toExponential, toFixed, toPrecision, toString
myString. // charAt, charCodeAt, concat, indexOf, lastIndexOf, length and many more...
Antwort #3
In TypeScript kann man mithilfe eines Markers ungefähr das erreichen, was manchmal als opaquer Typ bezeichnet wird.
// Helper for generating Opaque types.
type Opaque<T, K> = T & { __opaque__: K };
// 2 opaque types created with the helper
type Int = Opaque<number, 'Int'>;
type ID = Opaque<number, 'ID'>;
// using our types to differentiate our properties even at runtime
// they are still just numbers
class Foo {
someId: ID;
someInt: Int;
}
let foo = new Foo();
// compiler won't let you do this due to or markers
foo.someId = 2;
foo.someInt = 1;
// when assigning, you have to cast to the specific type
// NOTE: This is not completely type safe as you can trick the compiler
// with something like foo.someId = 1.45 as ID and it won't complain.
foo.someId = 2 as ID;
foo.someInt = 1 as Int;
// you can still consume as numbers
let sum: number = foo.someId + foo.someInt;
Dies ermöglicht es Ihnen, in Ihrem Code expliziter anzugeben, welche Typen Ihre Eigenschaften erwarten, und der Compiler erlaubt es Ihnen nicht, einen primitiven Wert ohne Typumwandlung zuzuweisen. Dies erzeugt keinen zusätzlichen .js-Ausgabe, und Sie können die Werte weiterhin in dem Typ verwenden, den sie haben. In diesem Beispiel verwende ich Zahlen, aber Sie können auch Zeichenketten und andere Typen verwenden.
Sie können den Compiler in diesem Beispiel immer noch dazu verleiten, etwas zu akzeptieren, das keine Int- oder Id-Variable ist, aber es sollte auffallen, wenn Sie versuchen würden, 1,45 als Int oder so etwas zuzuweisen. Sie haben auch die Möglichkeit, Hilfsfunktionen zu erstellen, die Sie zur Erstellung Ihrer Werte verwenden, um eine Laufzeitüberprüfung bereitzustellen.
Es gibt eine Reihe verschiedener Möglichkeiten, wie man "gekennzeichnete" Typen erstellen kann. Hier ist ein guter Artikel: https://michalzalecki.com/nominal-typing-in-typescript/
Antwort #4
Dies ist das beste Ergebnis auf Google für mich, also denke ich, dass ich die Lösungen, die ich gefunden habe, bereitstellen sollte.
Verwendung von bigint
Jetzt, wo es 2020 ist und bigint akzeptiert wurde, verdient es eine Erwähnung. Sie können einfach das Folgende tun. Beachten Sie, dass bigints im Vergleich zu einem number eine größere Auswirkung auf die Leistung haben.
const myNumber: bigint = 10n
Verwendung eines nominalen Typs/getaggten Typs/undurchsichtigen Typs
Eine Alternative ist die Verwendung eines Nominaltyps, aber er ist möglicherweise weniger ergonomisch, und ich bin mir nicht sicher, ob er schneller als bigint ist. Das Muster lässt sich jedoch auf jeden Typ verallgemeinern, nicht nur auf number. TypeScript hat keine »First-Class«-Unterstützung dafür, also muss man einen kleinen Trick anwenden. Es gibt eine Bibliothek dafür namens newtype-ts, die gängige Typen wie Integer enthält, sodass man diese einfach verwenden könnte, aber ich werde die Funktionsweise unten erklären.
Zunächst definieren wir den integer-Typ.
const TAG = Symbol()
type integer = number & { readonly [TAG]: unique symbol }
Der TAG stellt sicher, dass wir einen einzigartigen Wert haben, damit wir nicht versehentlich ein Objekt mit demselben Schlüssel erstellen, und wir machen das Feld aus demselben Grund ebenfalls zu einem einzigartigen Symbol. Nun wird Ihr Integer dieses Objektfeld tatsächlich nicht haben, aber das ist in Ordnung.
Damit können Sie immer noch integer zu number mit + hinzufügen. Nicht gut. Sie können hier die Typsicherheit der Argumente erzwingen, indem Sie das Typsystem mit einer Funktion anpassen. Ich werde sie einfach guard nennen, und wie Sie sehen können, ist sie wieder nicht speziell für integer - Sie könnten weitere undurchsichtige Typen erstellen und dies erneut verwenden.
type guard = <A>(f: (...ns: Array<A>) => A, ...ns: Array<A>) => A
const guard: guard = (f, ...ns) => f(...ns)
Wenn Sie versuchen, das mit einer Zahl aufzurufen
const bad: integer = guard((a, b) => a + b as integer, myCoolInteger, 10)
Sie erhalten einen Fehler wie unten
Argument of type '10' is not assignable to parameter of type 'integer'.
Type '10' is not assignable to type '{ readonly [TAG]: unique symbol; }'.(2345)
Beachten Sie, dass Sie den Rückgabewert hier nicht erzwingen (weil Sie as integer verwenden müssen) und einige Operatoren wie / Gleitkommazahlen zurückgeben, sodass Sie wahrscheinlich dennoch Laufzeitüberprüfungen durchführen oder eine Math.round zu einer spezialisierten Version von guard hinzufügen möchten, aber dies wird zumindest sicherstellen, dass Sie nicht versuchen, zwei verschiedene numerische Typen zusammen zu verwenden - stellen Sie sich vor, Sie haben GBP und USD und versuchen, diese zu addieren, das wäre wahrscheinlich nicht das, was Sie beabsichtigten.
Antwort #5
Es gibt in TypeScript keinen integer oder float, sondern nur den number-Typ wie in JavaScript. Wenn Sie jedoch dem Programmierer sagen möchten, dass Sie den integer-Typ erwarten, können Sie versuchen, Typ-Aliasen wie
type integer = number;
type float = number;
// example:
function setInt(id: integer) {}
aber dies ist immer noch vom Typ number und Sie können float erhalten.
Teil der Beschreibung aus der Dokumentation:
"Aliasing erzeugt tatsächlich keinen neuen Typ - es erstellt einen neuen Namen, um auf diesen Typ zu verweisen. Das Aliasing eines Primitivs ist nicht besonders nützlich, kann aber als eine Form der Dokumentation verwendet werden."
