Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you specify that a class property is an integer?

Tags:

typescript

People also ask

What is integer in TypeScript?

In TypeScript, there is no concept of integer data types like in other programming languages. Only a number type is used to represent floating-point numbers in general.

Does TypeScript have an integer type?

Note − There is no integer type in TypeScript and JavaScript.

What is the difference between number and number in TypeScript?

number Vs Number The primitive data type does not have properties or methods. The Number is a wrapper object around number . It is created by using the syntax new Number(value) .


  1. I think there is not a direct way to specify whether a number is integer or floating point. In the TypeScript specification section 3.2.1 we can see:

    "...The Number primitive type corresponds to the similarly named JavaScript primitive type and represents double-precision 64-bit format IEEE 754 floating point values..."

  2. I think int is a bug in Visual Studio intelliSense. The correct is number.


TypeScript is a superset of JavaScript, which doesn't have a concept of an int. It only has the concept of a number, which has a floating point.

Generally speaking, the amount of work the compiler would have to do to enforce only whole numbers for a TypeScript int type could potentially be massive and in some cases it would still not be possible to ensure at compile time that only whole numbers would be assigned, which is why it isn't possible to reliably add an int to TypeScript.

When you initially get intelliSense in Visual Studio, it isn't possible for the tooling to determine what to supply, so you get everything, including int - but once you are dealing with something of a known type, you'll get sensible intelliSense.

Examples

var myInt: number;
var myString: string;

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

In TypeScript you can approximate what is sometimes called an opaque type using a marker.

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

Doing this allow you to be more explicit in your code as to what types your properties expect, and the compiler won't allow you to assign a primitive value without a cast. This doesn't produce any additional .js output, and you can still consume and use the values as whatever types they are based on. In this example I'm using numbers, but you can use on strings and other types as well.

You can still trick the compiler into accepting something that isn't an Int or an Id in this example, but it should jump out if you were trying to assign 1.45 as Int or something like that. You also have the option of creating helper functions that you use to create your values to provide runtime validation.

There's a number of different ways you can create "marked" types. Here's a good article: https://michalzalecki.com/nominal-typing-in-typescript/


There is no integer or float but number type in TypeScript like in JavaScript. But if you want tell programmer that you expect integer type you can try to use Type Aliases like

type integer = number;
type float = number;

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

but this is still number type and you can get float.

Part of description from documentation:
"Aliasing doesn’t actually create a new type - it creates a new name to refer to that type. Aliasing a primitive is not terribly useful, though it can be used as a form of documentation."


This is the top result on Google for me so I figure I should provide the solutions I found.

Using bigint

Now that it's 2020 and bigint has been accepted, it deserves a mention. You can simply do the below. Beware that bigints come with a bigger performance impact compared to a number.

const myNumber: bigint = 10n


Using a nominal type / tagged type / opaque type

An alternative is to use a nominal type, but it's arguably less ergonomic and I'm not sure if it's any faster than bigint, but the pattern does generalise to any type, not just number. TypeScript doesn't have "first-class" support for this so you have to do a cheeky hack. There's a library for this called newtype-ts that includes common types like Integer so you might just want to just use that, but I'll explain the workings below.

To start out with we define the integer type.

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

The TAG ensures we have a unique value so that we don't accidentally make an object with the same key, and we make the field a unique symbol too for the same reason. Now, your integer won't actually have this object field but that's fine.

With this you can still add integer to number using +. Not good. So you can enforce type safety on the arguments here by massaging the type system with a function. I'm just gonna call it guard, and again as you can see it isn't specific to integers – you could make more opaque types and use this again.

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

If you try to call that with a number

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

you'll get an error like below

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

Note that you aren't enforcing the return type here (because you have to use as integer) and some operators like / will return floating point numbers so you probably still want to do runtime checks or add a Math.round to a specialised version of guard, but this will at least ensure you're not trying to use two separate numeric types together – imagine you have GBP and USD and try to add those, that would likely not be what you intended.


AssemblyScript supports integer values (with its asm.js backend). You can generate a JavaScript file using --jsFile.

To do it manually, you can force a value to be an integer by adding | 0.

This improves the performance because the browser engine is now able to use integers, which are faster than floats in the operations.

Say you intend to write:

function add(a: i32, b: i32) {
  return a + b
}

You should write it as the following

type i32 = number
function add(a: i32, b: i32) {
  a = a | 0
  b = b | 0
  return a + b
}

or in JavaScript (this is what asc --jsFile generates)

function add(a, b) {
  a = a | 0
  b = b | 0
  return a + b
}

For class properties:

type i32 = number
class MyClass {
  a: i32 = a | 0
}