Jak określić, że właściwość klasy jest liczbą całkowitą?


107

Eksperymentuję z TypeScripti podczas tworzenia klasy z IDpolem, które powinno być integer, trochę się pogubiłem.

Po pierwsze, w Visual Studio 2012 z wtyczką TypeScript widzę intna liście typów intelliSense. Ale pojawia się błąd kompilacji, który mówi:

nazwa „int” nie istnieje w bieżącym zakresie.

Sprawdziliśmy specyfikacje językowe i zobaczyć tylko następujące typy pierwotne: number, string, boolean, null, i undefined. Brak integertypu.

Zostałem więc z dwoma pytaniami:

  1. Jak mam wskazać użytkownikom mojej klasy, że dane pole to nie tylko a, numberale integer(i nigdy nie jest floatingpunktem lub decimalliczbą)?

  2. Dlaczego widzę intna liście Intellisense, jeśli nie jest to prawidłowy typ?

Aktualizacja: Wszystkie odpowiedzi, które otrzymałem do tej pory, dotyczą tego, jak JavaScriptnie ma typu int, trudno byłoby wymusić inttyp w czasie wykonywania ... Wiem to wszystko. Pytam, czy istnieje TypeScriptsposób na zapewnienie użytkownikom mojej klasy adnotacji, że to pole powinno być integer. Może komentarz do jakiegoś konkretnego formatu?


6
Nie, i to też nie jest prawidłowy identyfikator w mojej aplikacji. 10 byłby prawidłowym identyfikatorem.
— Josh


Jednym ze sposobów walidacji pola i upewnienia się, że akceptowane są tylko wartości całkowite, jest utworzenie metody walidacji, która najpierw rzutuje dowolną potencjalną wartość wejściową na ciąg znaków, używa wyrażenia RegEx do sprawdzenia, czy wartość a.) Nie ma kropki dziesiętnej lub b.) jeśli istnieje przecinek dziesiętny, wszystkie kolejne znaki są zerami. Następnie metoda rzutuje z powrotem do typu liczbowego, a wynik jest przekazywany do pola, które wymaga ograniczenia.
— Nathan Toulbert

Odpowiedzi:


107
  1. Myślę, że nie ma bezpośredniego sposobu określenia, czy liczba jest liczbą całkowitą, czy zmiennoprzecinkową. W sekcji specyfikacji TypeScript 3.2.1 możemy zobaczyć:

    „... Typ pierwotny Number odpowiada podobnie nazwanemu typowi prymitywu JavaScript i reprezentuje wartości zmiennoprzecinkowe w 64-bitowym formacie podwójnej precyzji IEEE 754 ...”

  2. Myślę, że intjest to błąd w IntelliSense programu Visual Studio. Prawidłowe jest number.


2
Myślę, że to jest odpowiedź. 1) Nie można określić nic bardziej szczegółowego niż po prostu „liczba” dla typów liczbowych, ani nie ma też żadnej rozpoznawalnej adnotacji, która by to zrobiła; oraz 2) we wtyczce VS jest błąd sugerujący nieprawidłowe typy, być może na podstawie listy zarezerwowanych słów kluczowych ECMA 262 (która zawiera „int”)
— Josh

Możliwe jest zaimplementowanie statycznego typu int, ale zdecydują się tego nie robić. Może uważają, że jest to zbyt skomplikowane do wdrożenia?
— trusktr

29

TypeScript to nadzbiór języka JavaScript, który nie ma pojęcia int. Ma tylko pojęcie liczby, która ma zmiennoprzecinkowe.

Filozoficznie, ilość pracy, jaką kompilator musiałby wykonać, aby wymusić tylko liczby całkowite dla typu int TypeScript, może być potencjalnie ogromna, aw niektórych przypadkach nadal nie byłoby możliwe zapewnienie w czasie kompilacji, że przypisywane będą tylko liczby całkowite, co dlatego nie można wiarygodnie dodać intdo TypeScript.

Kiedy początkowo uzyskasz intelliSense w programie Visual Studio, nie jest możliwe, aby narzędzia określiły, co ma dostarczyć, więc otrzymasz wszystko, w tym int - ale gdy masz do czynienia z czymś znanego typu, otrzymasz rozsądny intelliSense.

Przykłady

var myInt: number;
var myString: string;

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

2
Ale to nie odpowiada na moje pytanie, jak dodawać adnotacje do mojej klasy, aby użytkownicy znali właściwy typ pola. Ponadto, jeśli „int” nigdy nie jest prawidłowym typem, dlaczego znajduje się na liście „wszystko”?
— Josh

Używasz public myVariable : number;do zadeklarowania tego jako liczby. intjest słowem zastrzeżonym w JavaScript, dlatego pojawia się na liście wszystkiego. Informacje o JavaScript int: javascript.about.com/od/reference/g/rint.htm
— Fenton

public discountPercent : numbernie informuje użytkownika, czy powinna to być liczba dziesiętna (np. 0,10 dla 10%), czy liczba całkowita (np. 10 dla 10%). Czy nie ma sposobu, aby przypisać użytkownikowi biblioteki, co jest ważne, a co nie?
— Josh

@JoshuaBeall - nie ma rozróżnienia między liczbami dziesiętnymi i całkowitymi w JavaScript lub TypeScript. Typ liczby jest używany w obu przypadkach.
— Fenton

1
@Fenton, powiedziałeś: „TypeScript jest nadzbiorem JavaScript, który nie ma pojęcia int. Ma tylko pojęcie liczby, która ma zmiennoprzecinkowe.”. To prawda, ale w rzeczywistości jest możliwe zaimplementowanie inttypu w TypeScript, tylko że zdecydowali się tego nie robić.
— trusktr

15

Nie ma integerlub floatale numberwpisz w TypeScript, jak w JavaScript. Ale jeśli chcesz powiedzieć programiście, że oczekujesz integertypu, możesz spróbować użyć aliasów typów, takich jak

type integer = number;
type float = number;

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

ale to nadal jest numbertyp i możesz dostać float.

Część opisu z dokumentacji:
„Aliasing tak naprawdę nie tworzy nowego typu - tworzy nową nazwę odnoszącą się do tego typu. Aliasowanie prymitywu nie jest zbyt użyteczne, chociaż może być używane jako forma dokumentacji.”


8

W języku TypeScript można przybliżyć to, co czasami nazywa się typem nieprzezroczystym, używając znacznika.

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

Dzięki temu możesz bardziej jednoznacznie określić w kodzie, jakich typów oczekują twoje właściwości, a kompilator nie pozwoli ci przypisać wartości pierwotnej bez rzutowania. Nie generuje to żadnych dodatkowych danych wyjściowych .js i nadal można używać i używać wartości jako typów, na których są oparte. W tym przykładzie używam liczb, ale możesz ich używać również na napisach i innych typach.

W tym przykładzie nadal możesz oszukać kompilator, aby zaakceptował coś, co nie jest Intem ani Id, ale powinno wyskoczyć, jeśli próbujesz przypisać 1,45 jako Int lub coś w tym stylu. Masz również możliwość tworzenia funkcji pomocniczych, których używasz do tworzenia wartości w celu zapewnienia walidacji w czasie wykonywania.

Typy „zaznaczone” można tworzyć na wiele różnych sposobów. Oto dobry artykuł: https://michalzalecki.com/nominal-typing-in-typescript/


Jak powiedziano w innym miejscu, faktyczna użyteczność tego jest nieco wątpliwa, ale możesz pójść dalej, dodając typeguard i / lub Casting-Helper, co będzie łatwe w przypadku niektórych rzeczy - takich jak przykład Int - i prawie niemożliwe w przypadku innych - takich jak przykład ID: // @ts-ignore const isInt = (val: number | Int): val is Int => !(val % 1); const Integer = (val: number): Int => Math.round(val) as any;
— Sam96,

5

To dla mnie najlepszy wynik w Google, więc myślę, że powinienem przedstawić rozwiązania, które znalazłem.

Za pomocą bigint

Teraz, gdy jest rok 2020 i bigintzostał zaakceptowany, zasługuje na wzmiankę. Możesz po prostu wykonać poniższe czynności. Pamiętaj, że bigintma to większy wpływ na wydajność w porównaniu z plikiem number.

const myNumber: bigint = 10n


Używanie typu nominalnego / oznaczonego / nieprzezroczystego

Alternatywą jest użycie typu nominalnego, ale jest on prawdopodobnie mniej ergonomiczny i nie jestem pewien, czy jest szybszy niż bigint, ale wzór uogólnia się na każdy typ, nie tylko number. TypeScript nie obsługuje tego typu „pierwszej klasy”, więc musisz zrobić bezczelny hack. Jest do tego biblioteka o nazwie, newtype-tsktóra zawiera typowe typy, Integerwięc możesz po prostu chcieć tego użyć, ale wyjaśnię poniżej działanie.

Na początek definiujemy integertyp.

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

Te TAG, zapewnia mamy unikalną wartość tak, że nie przypadkowo tworzenia obiektu z tego samego klucza, i robimy pole unikalny symbol też z tego samego powodu. Twoja liczba całkowita nie będzie miała tego pola obiektu, ale to dobrze.

Dzięki temu możesz nadal dodawać integerdo numberusing +. Niedobrze. Możesz więc wymusić bezpieczeństwo typów argumentów tutaj, masując system typów za pomocą funkcji. Nazwę to po prostu strażnikiem i znowu, jak widać, nie jest to specyficzne dla integers - możesz zrobić bardziej nieprzezroczyste typy i użyć tego ponownie.

type guard = <A>(f: (...ns: Array<A>) => A, ...ns: Array<A>) => A
const guard: guard = (f, ...ns) => f(...ns)

Jeśli spróbujesz to nazwać za pomocą number

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

pojawi się błąd jak poniżej

Argument of type '10' is not assignable to parameter of type 'integer'.
  Type '10' is not assignable to type '{ readonly [TAG]: unique symbol; }'.(2345)

Zauważ, że nie wymuszasz tutaj zwracanego typu (ponieważ musisz go użyć as integer), a niektóre operatory, takie jak /, zwracają liczby zmiennoprzecinkowe, więc prawdopodobnie nadal chcesz sprawdzić w czasie wykonywania lub dodać a Math.rounddo wyspecjalizowanej wersji guard, ale to przynajmniej upewnić się, że nie próbujesz użyć dwóch odrębnych typów numerycznych razem - wyobraź sobie, że masz GBPa USDi spróbuj dodać te, które nie mogą być, co zamierzał.


2

Cóż, jak widzieliście, maszynopis nie zawiera typu danych typu float, takiego jak język javascript. Tylko mają number, że pokrycie wszystkich inti doublew tym samym czasie; może musisz utworzyć funkcję, która pobiera liczbę i sprawdzić, czy jest to intlub double, zwracając jakiś stan w przypadku błędu / sukcesu. Coś takiego jako metoda Twojej klasy:

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
}

Uwaga: to nie działa 10.0np. Jeśli naprawdę tego nie chcesz, może musisz przekształcić to w łańcuch i spróbować znaleźć ..


Pomogłoby to w egzekwowaniu w czasie wykonywania, ale przede wszystkim interesuje mnie poinformowanie użytkownika, jakie liczby są prawidłowe.
— Josh

1

intbył zarezerwowany do wykorzystania w przyszłości we wcześniejszych wersjach javascript (jeśli wolisz ECMAScript). Ale teraz jest to poprawne słowo (gdzie „teraz” oznacza „w najnowszej specyfikacji”).

Na przykład w 262 było nadal zastrzeżone, http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf

Byłoby fajnym dodatkiem do skryptu, gdyby intzaimplementowano typ danych, ale z dostępnymi wszystkimi regułami sprawdzania typów i rzutowania w czasie kompilacji.


Więc może intelisense wychwytuje to „zastrzeżone słowo”, mimo że nie jest rozpoznawane przez kompilator?
— Josh

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.