I've been recently working on a project using TypeScript, and—like many-a-developer before me—ran into a scenario where I determined it would be useful to have an Integer type which will throw an error if you attempt to store a number containing a decimal point in it.
In effort to see if such a thing was possible, I began looking into and learning about TypeScript's Utility types, which in turn led me to Conditional types. At first glance it seemed like this was the answer I was looking for, however after some playing around I'm starting to get the impression that conditional types can't be used to achieve my desired effect. I decided it would be best to post this question to get some clarification on these concepts before toiling away for several more hours trying to build something that isn't possible with TypeScript's given toolset.
In effect, I would like to define a type which could be used like follows:
let myVar: Integer;
myVar = 5;
myVar = 5.5; // This line should throw a compile-time error
Below we can see my first attempt at making an Integer type using conditional typing:
type Integer<T extends number> = `${T}` extends `${number}.${number}` ? never : T;
The intended logic was:
Create a type whose value must be a number type, but when that number is converted to a string, it can't follow the pattern of "number.number" (there can't be a decimal point).
I then went to try testing out my new type before realizing the error in my logic. Because I'm setting the condition based off a generic input for the Integer type itself, that condition only applies to whatever value is supplied to the type when it is being "assigned" to a variable at its declaration. For instance:
type Integer<T extends number> = `${T}` extends `${number}.${number}` ? never : T;
let myVar: Integer<5>; // I supply 5 as the generic value for the Integer type
myVar = 5; // I can then assign myVar to the value 5 because it matches the type I provided for the generic when designating that myVar is of type Integer
myVar = 6; // If I try to assign myVar to an integer value other than the one I provided for the generic, I get a compile-time error
let myVar2: Integer<5.5>; // I supply 5.5 as the generic value for the Integer type
myVar2 = 5.5; // If I try to assign myVar2 to the value 5.5, I get a compile-time error because type number can't be assigned to type never
So instead of creating a type which constricts the values it can take to only being Integers, what I actually created is a type which constricts the values it can take to only being the specific integer specified when the variable is declared.
In referencing documentation and guides on conditional types, I found that often the presented conditional types weren't being used as a type for variables, but rather they were being utilized in defining other types. For instance, take the following example code excerpt (from https://blog.logrocket.com/guide-conditional-types-typescript/):
type ExtractIdType<T extends {id: string | number}> = T["id"]
interface NumericId {
id: number
}
interface StringId {
id: string
}
interface BooleanId {
id: boolean
}
type NumericIdType = ExtractIdType<NumericId> // type NumericIdType = number
type StringIdType = ExtractIdType<StringId> // type StringIdType = string
type BooleanIdType = ExtractIdType<BooleanId> // won't work
This sort of usage is vastly different to how I an trying to use conditional types, and is what sparked my initial thoughts that they might not serve the purpose that I originally was anticipating them to.
So, as stated above I decided to come here to see if my suspicions are correct that it isn't possible to use conditional types to achieve my desired result.
I think you just need the type
bigintno? It's built into TypeScript.