Wie legen Sie fest, dass eine Klasseneigenschaft eine Ganzzahl ist?


96

Ich experimentiere mit TypeScript und beim Erstellen einer Klasse mit einem "ID" -Feld, das eine Ganzzahl sein sollte, bin ich etwas verwirrt.

Zunächst sehe ich in Visual Studio 2012 mit dem TypeScript-Plugin "int" in der IntelliSense-Typenliste. Es wird jedoch ein Kompilierungsfehler angezeigt, der besagt, dass der Name 'int' im aktuellen Bereich nicht vorhanden ist.

Ich habe die Sprachspezifikationen überprüft und sehe nur die folgenden primitiven Typen: number, string, boolean, null und undefined. Kein ganzzahliger Typ.

Ich habe also zwei Fragen:

  1. Wie soll ich Benutzern meiner Klasse anzeigen, dass ein bestimmtes Feld nicht nur eine "Zahl", sondern eine Ganzzahl (und niemals eine Gleitkomma- oder Dezimalzahl) ist?

  2. Warum wird "int" in der Intellisense-Liste angezeigt, wenn es sich nicht um einen gültigen Typ handelt?

Update: Alle Antworten, die ich bisher erhalten habe, beziehen sich darauf, dass JavaScript keinen int-Typ hat. Es wäre schwierig, einen int-Typ zur Laufzeit zu erzwingen. Ich weiß das alles. Ich frage, ob es eine TypeScript-Möglichkeit gibt, Benutzern meiner Klasse eine Anmerkung zu geben, dass dieses Feld eine Ganzzahl sein sollte. Vielleicht ein Kommentar eines bestimmten Formats?


6
Nein, und es ist auch keine gültige ID in meiner App. 10 wäre eine gültige ID.
— Josh

Antworten:


99
  1. Ich denke, es gibt keine direkte Möglichkeit, anzugeben, ob eine Zahl eine Ganzzahl oder ein Gleitkomma ist. In der TypeScript-Spezifikation in Abschnitt 3.2.1 sehen wir:

    "... Der Primitivtyp" Zahl "entspricht dem ähnlich benannten Primitivtyp" JavaScript "und repräsentiert IEEE 754-Gleitkommawerte im 64-Bit-Format mit doppelter Genauigkeit ..."

  2. Ich denke, es intist ein Fehler in Visual Studio IntelliSense. Das richtige ist number.


2
Ich denke das ist die Antwort. 1) Sie können für numerische Typen nichts detaillierteres als nur "Zahl" angeben, und es gibt auch keine anerkannte Anmerkung dazu. und 2) es gibt einen Fehler im VS-Plugin, der ungültige Typen vorschlägt, möglicherweise basierend auf der ECMA 262-Liste reservierter Schlüsselwörter (einschließlich "int")
— Josh

Es ist möglich, den statischen int-Typ zu implementieren, aber sie entscheiden sich dagegen. Vielleicht denken sie, dass es zu komplex ist, um es umzusetzen?
— Trusktr

28

TypeScript ist eine Obermenge von JavaScript, die kein Int-Konzept hat. Es hat nur das Konzept einer Zahl, die einen Gleitkomma hat.

Philosophisch gesehen könnte der Arbeitsaufwand, den der Compiler leisten müsste, um nur ganze Zahlen für einen TypeScript-Int-Typ zu erzwingen, möglicherweise enorm sein, und in einigen Fällen wäre es immer noch nicht möglich, zum Zeitpunkt der Kompilierung sicherzustellen, dass nur ganze Zahlen zugewiesen werden Aus diesem Grund ist es nicht möglich int, TypeScript zuverlässig zu ergänzen .

Wenn Sie IntelliSense zum ersten Mal in Visual Studio erhalten, kann das Tool nicht bestimmen, was bereitgestellt werden soll. Sie erhalten also alles, einschließlich int. Sobald Sie sich jedoch mit einem bekannten Typ befassen, erhalten Sie eine sinnvolle IntelliSense.

Beispiele

var myInt: number;
var myString: string;

myInt. // toExponential, toFixed, toPrecision, toString
myString. // charAt, charCodeAt, concat, indexOf, lastIndexOf, length and many more...

2
Das beantwortet jedoch nicht meine Frage, wie ich meine Klasse mit Anmerkungen versehen soll, damit Benutzer den richtigen Feldtyp kennen. Wenn "int" niemals ein gültiger Typ ist, warum wird er dann in die Liste "alles" aufgenommen?
— Josh

Sie verwenden public myVariable : number;es, um es als Nummer zu deklarieren. intist ein reserviertes Wort in JavaScript, weshalb es in der Liste "Alles" angezeigt wird. Infos zu JavaScript int: javascript.about.com/od/reference/g/rint.htm
— Fenton

public discountPercent : numberteilt dem Benutzer nicht mit, ob es sich um eine Dezimalzahl (z. B. 0,10 für 10%) oder eine Ganzzahl (z. B. 10 für 10%) handeln soll. Gibt es keine Möglichkeit, dem Benutzer der Bibliothek mitzuteilen, was gültig ist und was nicht?
— Josh

@JoshuaBeall - In JavaScript oder TypeScript gibt es keine Unterscheidung zwischen Dezimal und Ganzzahl. Der Nummerntyp wird für beide verwendet.
— Fenton

1
@Fenton Sie sagten: "TypeScript ist eine Obermenge von JavaScript, die kein Konzept eines Int hat. Es hat nur das Konzept einer Zahl, die einen Gleitkomma hat." Stimmt, aber es ist tatsächlich möglich, einen intTyp in TypeScript zu implementieren , nur dass sie sich dagegen entschieden haben.
— Trusktr

15

Es gibt kein integeroder floatnur numberTypeScript wie in JavaScript. Wenn Sie dem Programmierer jedoch mitteilen möchten, dass Sie einen integerTyp erwarten , können Sie versuchen, Typ-Aliase wie zu verwenden

type integer = number;
type float = number;

// example:
function setInt(id: integer) {}

aber das ist noch numbertyp und du kannst bekommen float.

Teil der Beschreibung aus der Dokumentation:
"Aliasing erstellt keinen neuen Typ - es wird ein neuer Name erstellt, der auf diesen Typ verweist. Aliasing eines Grundelements ist nicht besonders nützlich, obwohl es als Dokumentationsform verwendet werden kann."


8

In TypeScript können Sie mithilfe eines Markers einen manchmal als undurchsichtig bezeichneten Typ approximieren.

// 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;

Auf diese Weise können Sie in Ihrem Code genauer angeben, welche Typen Ihre Eigenschaften erwarten, und der Compiler ermöglicht es Ihnen nicht, einen primitiven Wert ohne Umwandlung zuzuweisen. Dies erzeugt keine zusätzliche .js-Ausgabe, und Sie können die Werte weiterhin als die Typen verwenden, auf denen sie basieren. In diesem Beispiel verwende ich Zahlen, aber Sie können sie auch für Zeichenfolgen und andere Typen verwenden.

Sie können den Compiler dennoch dazu verleiten, etwas zu akzeptieren, das in diesem Beispiel kein Int oder keine ID ist. Es sollte jedoch herausspringen, wenn Sie versuchen, 1.45 als Int oder ähnliches zuzuweisen. Sie haben auch die Möglichkeit, Hilfsfunktionen zu erstellen, mit denen Sie Ihre Werte für die Laufzeitüberprüfung erstellen.

Es gibt verschiedene Möglichkeiten, "markierte" Typen zu erstellen. Hier ist ein guter Artikel: https://michalzalecki.com/nominal-typing-in-typescript/


Wie bereits an anderer Stelle erwähnt, ist die tatsächliche Verwendbarkeit etwas fraglich, aber Sie können noch weiter gehen, indem Sie einen Typeguard und / oder einen Casting-Helfer hinzufügen, was für einige Dinge - wie das Int-Beispiel - einfach und für andere so gut wie unmöglich ist das ID-Beispiel: // @ts-ignore const isInt = (val: number | Int): val is Int => !(val % 1); const Integer = (val: number): Int => Math.round(val) as any;
— Sam96

4

Dies ist für mich das beste Ergebnis bei Google. Ich denke, ich sollte die Lösungen bereitstellen, die ich gefunden habe.

Verwenden von bigint

Jetzt, da es 2020 bigintist und akzeptiert wurde, verdient es eine Erwähnung. Sie können einfach das Folgende tun. Beachten Sie, dass dies biginteine größere Auswirkung auf die Leistung hat als a number.

const myNumber: bigint = 10n


Verwenden eines nominalen Typs / markierten Typs / undurchsichtigen Typs

Eine Alternative ist die Verwendung eines nominalen Typs, aber er ist wohl weniger ergonomisch und ich bin mir nicht sicher, ob er schneller ist als bigint, aber das Muster lässt sich auf jeden Typ verallgemeinern, nicht nur number. TypeScript bietet hierfür keine "erstklassige" Unterstützung, daher müssen Sie einen frechen Hack ausführen. Es gibt eine Bibliothek dafür newtype-ts, die gängige Typen enthält, Integerso dass Sie diese vielleicht nur verwenden möchten, aber ich werde die Funktionsweise unten erklären.

Zunächst definieren wir den integerTyp.

const TAG = Symbol()
type integer = number & { readonly [TAG]: unique symbol }

Das TAG garantieren , dass wir haben einen einzigartigen Wert , so dass wir nicht versehentlich ein Objekt mit dem gleichen Schlüssel machen, und wir machen das Feld ein einzigartiges Symbol auch aus dem gleichen Grunde. Nun, Ihre Ganzzahl hat dieses Objektfeld nicht wirklich, aber das ist in Ordnung.

Damit können Sie noch integerzur numberVerwendung hinzufügen +. Nicht gut. Sie können also die Typensicherheit für die Argumente hier erzwingen, indem Sie das Typsystem mit einer Funktion massieren. Ich werde es nur Wache nennen, und wie Sie sehen, ist es nicht spezifisch für integers - Sie könnten undurchsichtigere Typen erstellen und diese wieder 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 einem zu nennen number

const bad: integer = guard((a, b) => a + b as integer, myCoolInteger, 10)

Sie erhalten eine Fehlermeldung 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ückgabetyp hier nicht erzwingen (weil Sie ihn verwenden müssen as integer) und einige Operatoren wie /Gleitkommazahlen zurückgeben, sodass Sie wahrscheinlich weiterhin Laufzeitprüfungen durchführen oder Math.roundeiner speziellen Version von a hinzufügen möchten, dies guardjedoch zumindest sicherzustellen , dass Sie nicht zu verwenden , um zwei getrennte numerische Typen zusammen versuchen - stellen Sie sich haben GBPund USDund versuchen , hinzuzufügen , die, das wäre wahrscheinlich nicht das, was Sie bestimmt.


2

Wie Sie gesehen haben, hat Typoskript keinen Float-Datentyp wie die Javascript-Sprache. Haben Sie nur die number, die alle intund doublegleichzeitig abdecken ; Vielleicht müssen Sie eine Funktion erstellen, die eine Zahl annimmt, und überprüfen, ob es sich um eine intoder handelt double, indem Sie einen Status zurückgeben, falls Fehler / Erfolg auftreten. So etwas als Methode Ihrer Klasse:

function SetN(x:number) {
   var is_int = parseInt(x) === parseFloat(x);
   if(is_int) this.n = x;
   return is_int;
}

//..
y = 10.5;
if(SetN(y)) {
  //OK
} else {
   //error not set y isn't a int
}

Hinweis: Es funktioniert nicht für 10.0z. B. Wenn Sie es nicht wirklich wollen, müssen Sie es möglicherweise in einen String konvertieren und versuchen, einen zu finden ..


Dies würde zur Durchsetzung der Laufzeit beitragen, aber ich bin hauptsächlich daran interessiert, den Benutzer darüber zu informieren, welche Nummern gültig sind.
— Josh

1

intwar für die zukünftige Verwendung des Schlüsselworts in früheren Versionen von Javascript reserviert (ECMAScript, wenn Sie es vorziehen). Aber es ist jetzt ein gültiges Wort (wobei "jetzt" gleich "in der neuesten Spezifikation" ist).

Zum Beispiel war es 262 noch reserviert, http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf

Es wäre eine schöne Ergänzung zu Typoskript, wenn ein intDatentyp implementiert wäre, aber alle Regeln zur Typprüfung und zur Umwandlung zur Kompilierungszeit verfügbar wären .


Vielleicht nimmt der Intellisense dieses "reservierte Wort" auf, obwohl es vom Compiler nicht erkannt wird?
— Josh

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.