Как указать, что свойство класса является целым числом?


107

Я экспериментирую TypeScriptи в процессе создания класса с IDполем, которое должно быть integer, я немного запутался.

Во-первых, в Visual Studio 2012 с плагином TypeScript я вижу intв списке типов intelliSense. Но я получаю сообщение об ошибке компиляции:

имя int не существует в текущей области.

Я рассмотрел язык спецификации и увидеть только следующие примитивные типы: number, string, boolean, null, и undefined. Нет integerтипа.

Итак, у меня осталось два вопроса:

  1. Как мне указать пользователям моего класса, что конкретное поле - это не просто, numberа integer(и никогда не floatingточка или decimalчисло)?

  2. Почему я вижу intв списке intellisense, если это недопустимый тип?

Обновление: все ответы, которые я получил до сих пор, касаются того, JavaScriptчто у меня нет типа int, было бы сложно обеспечить соблюдение intтипа во время выполнения ... Я все это знаю. Я спрашиваю, есть ли TypeScriptспособ предоставить пользователям моего класса аннотацию о том, что это поле должно быть integer. Может быть, комментарий какого-то определенного формата?


6
Нет, и в моем приложении это тоже недействительный идентификатор. 10 будет действительным идентификатором.
— Джош


Один из способов проверить поле и убедиться, что принимаются только целочисленные значения, - это создать метод проверки, который сначала переводит любое потенциальное входное значение в String, использует RegEx для проверки этого значения либо a.) Не имеет десятичной точки, либо б.) если десятичная точка существует, все последующие символы - нули. После этого метод возвращается к числовому типу, и результат передается в поле, которое требует ограничения.
— Натан Тулберт,

Ответы:


107
  1. Я думаю, что нет прямого способа указать, является ли число целым или плавающим. В разделе 3.2.1 спецификации TypeScript мы видим:

    "... Тип примитива Number соответствует одноименному типу примитива JavaScript и представляет собой значения с плавающей запятой в 64-битном формате двойной точности IEEE 754 ..."

  2. Я думаю, что intэто ошибка в Visual Studio intelliSense. Правильно number.


2
Думаю, это ответ. 1) Вы не можете указать что-либо более детальное, чем просто «число» для числовых типов, и при этом нет какой-либо распознанной аннотации для этого; и 2) в подключаемом модуле VS есть ошибка, предлагающая недопустимые типы, возможно, на основе списка зарезервированных ключевых слов ECMA 262 (который включает "int")
— Джош,

Можно реализовать статический тип int, но они этого не делают. Может, они думают, что это слишком сложно реализовать?
— trusktr 02

29

TypeScript - это надмножество JavaScript, в котором нет понятия int. Он имеет только понятие числа с плавающей запятой.

С философской точки зрения, объем работы, которую компилятор должен будет выполнить для обеспечения соблюдения только целых чисел для типа int TypeScript, потенциально может быть огромным, и в некоторых случаях все равно невозможно гарантировать во время компиляции, что будут назначены только целые числа, что вот почему невозможно надежно добавить intв TypeScript.

Когда вы изначально получаете intelliSense в Visual Studio, инструмент не может определить, что поставлять, поэтому вы получаете все, включая int, но как только вы имеете дело с чем-то известного типа, вы получите разумный intelliSense.

Примеры

var myInt: number;
var myString: string;

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

2
Но это не отвечает на мой вопрос о том, как аннотировать мой класс, чтобы пользователи знали правильный тип поля. Кроме того, если "int" никогда не является допустимым типом, почему он включен в список "все"?
— Джош

Вы используете, public myVariable : number;чтобы объявить его числом. int- зарезервированное слово в JavaScript, поэтому оно отображается в списке всего. Информация о JavaScript int: javascript.about.com/od/reference/g/rint.htm
— Фентон,

public discountPercent : numberне сообщает пользователю, должно ли оно быть десятичным (например, 0,10 для 10%) или целым числом (например, 10 для 10%). Нет ли возможности сообщить пользователю библиотеки, что действительно, а что нет?
— Джош

@JoshuaBeall - в JavaScript или TypeScript нет разницы между десятичным и целым числом. Тип числа используется для обоих.
— Fenton

1
@Fenton вы сказали: «TypeScript - это надмножество JavaScript, в котором нет понятия int. В нем есть только понятие числа, которое имеет плавающую точку». Верно, но на самом деле можно реализовать intтип в TypeScript, но они этого не сделали.
— trusktr 01

15

Там нет integerили , floatно numberтипа в машинописи , как в JavaScript. Но если вы хотите сказать программисту, что вы ожидаете integerтипа, вы можете попробовать использовать псевдонимы типа, например

type integer = number;
type float = number;

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

но это все еще numberтип и можно получить float.

Часть описания из документации:
«Псевдонимы на самом деле не создают новый тип - они создают новое имя для ссылки на этот тип. Использование псевдонимов для примитива не очень полезно, хотя его можно использовать как форму документации».


8

В TypeScript вы можете приблизительно определить то, что иногда называют непрозрачным типом, с помощью маркера.

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

Это позволит вам более четко указать в коде, какие типы ожидают ваши свойства, а компилятор не позволит вам присвоить примитивное значение без приведения. Это не дает никакого дополнительного вывода .js, и вы по-прежнему можете потреблять и использовать значения в качестве любых типов, на которых они основаны. В этом примере я использую числа, но вы также можете использовать их для строк и других типов.

Вы все еще можете обманом заставить компилятор принять что-то, что не является Int или Id в этом примере, но это должно выскочить, если вы пытались назначить 1,45 как Int или что-то в этом роде. У вас также есть возможность создавать вспомогательные функции, которые вы используете для создания своих значений для обеспечения проверки во время выполнения.

Есть несколько разных способов создания «помеченных» типов. Вот хорошая статья: https://michalzalecki.com/nominal-typing-in-typescript/


Как сказано в другом месте, фактическое удобство использования этого несколько сомнительно, но вы можете пойти дальше, добавив typeguard и / или Casting-Helper, что будет легко для некоторых вещей - например, в примере Int - и практически невозможно для других, например пример идентификатора: // @ts-ignore const isInt = (val: number | Int): val is Int => !(val % 1); const Integer = (val: number): Int => Math.round(val) as any;
— Sam96,

5

Для меня это лучший результат в Google, поэтому я считаю, что должен предоставить найденные мной решения.

С помощью bigint

Теперь bigint, когда наступил 2020 год, и он был принят, он заслуживает упоминания. Вы можете просто сделать следующее. Остерегайтесь того, что bigintони имеют большее влияние на производительность по сравнению с number.

const myNumber: bigint = 10n


Использование номинального типа / маркированного типа / непрозрачного типа

Альтернативой является использование номинального типа, но он, возможно, менее эргономичен, и я не уверен, что он быстрее bigint, но шаблон действительно распространяется на любой тип, а не только number. TypeScript не имеет «первоклассной» поддержки для этого, поэтому вам придется пойти на хитрость. Для этого есть библиотека, newtype-tsкоторая включает общие типы, например, Integerвы можете просто использовать ее, но я объясню работу ниже.

Для начала определим integerтип.

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

Это TAGгарантирует, что у нас есть уникальное значение, чтобы мы случайно не создали объект с тем же ключом, и мы также делаем поле уникальным символом по той же причине. Теперь у вашего целого числа на самом деле не будет этого поля объекта, но это нормально.

С этим вы все еще можете добавить integerк numberusing +. Фигово. Таким образом, вы можете обеспечить безопасность типов для аргументов здесь, массируя систему типов с помощью функции. Я просто назову это охранником, и опять же, как вы можете видеть, это не относится к integers - вы можете создать более непрозрачные типы и использовать это снова.

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

Если вы попытаетесь вызвать это с помощью number

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

вы получите ошибку, как показано ниже

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

Обратите внимание, что вы не применяете здесь возвращаемый тип (потому что вы должны использовать as integer), и некоторые операторы, например, /будут возвращать числа с плавающей запятой, поэтому вы, вероятно, все равно захотите выполнять проверки времени выполнения или добавлять Math.roundв специализированную версию guard, но это, по крайней мере, убедитесь, что вы не пытаетесь использовать два отдельных числовых типа вместе - представьте, что у вас есть GBPи, USDи попробуйте добавить те, которые, вероятно, не будут такими, как вы планировали.


2

Как вы видели, машинописный текст не имеет типа данных с плавающей запятой, такого как язык javascript. Только иметь то, numberчто покрывает все intи doubleодновременно; возможно, вам нужно создать функцию, которая принимает число и проверяет его, является ли оно intили double, возвращая какое-то состояние в случае ошибки / успеха. Что-то вроде этого как метод вашего класса:

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
}

Примечание: это не работает, 10.0например, если вы действительно этого не хотите, возможно, вам нужно преобразовать его в строку и попытаться найти ..


Это помогло бы обеспечить соблюдение правил выполнения, но меня прежде всего интересует информирование пользователя о том, какие числа действительны.
— Джош

1

intбыло зарезервировано для будущего использования в более ранних версиях JavaScript (если хотите, в ECMAScript). Но сейчас это подходящее слово (где «сейчас» равно «в последней спецификации»).

Например, в 262 году он все еще был зарезервирован, http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf

Было бы неплохо дополнить машинописный текст, если бы был intреализован тип данных, но со всеми доступными правилами проверки типов и приведением типов во время компиляции.


Так, возможно, intellisense улавливает это «зарезервированное слово», даже если оно не распознается компилятором?
— Джош

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.