Skip to content

Add basic types <Byte> and <Integer>Β #62867

Closed

Description

@bf

πŸ” Search Terms

typescript integer type
typescript integer limit
typescript byte type
typescript Uint8Array type

βœ… Viability Checklist

⭐ Suggestion

It would be nice to have basic types such as Byte or Uint8 that can match what is put into Uint8Array as default types

Currently using hacky wordaround to limit Byte type in typescript to 256 values.

type Byte = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255;

This might have been discussed already, and I found some stale issues where addition of integer type was explicitly excluded as design goal.

not sure if this is really related but it goes into same direction I think #62752

also the docs show one mention of Uint8Array and there is not even a data type definition in the docs. only thing I can find is: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-7.html which defines it as interface Uint8Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>.

Please nobody suggest to use single-item ArrayBuffer as a type for byte because people think in Integer and in Byte not in ArrayBuffer or Buffer.

πŸ“ƒ Motivating Example

right now let byte: number; could be float or anything. I don't use typescript to have floats where I expect bytes when working with things like Uint8Array

πŸ’» Use Cases

  1. Have an idiot proof, easy way to ensure that a value is a single byte

Activity

  1. MartinJohns commented on Dec 9, 2025

    @MartinJohns
    Contributor

    Duplicate of #195 / #4639.

  2. bf commented on Dec 9, 2025

    @bf
    Author

    Duplicate of #195 / #4639.

    Hi Martin, they were opened in 2014/2015 and both closed in 2018 - it's a long time ago.

    Not sure what the "mission statement" of typescript is nowadays, but if it is supposed to be a tool which makes code more robust it would be clever to add basic integer validation.

  3. jcalz commented on Dec 9, 2025

    @jcalz
    Contributor

    #54925 is the currently-open issue that encompasses this.

  4. bf commented on Dec 9, 2025

    @bf
    Author

    Unfortunately #54925 is also stale.

    Maybe this is some practical inside joke but how come that after all these years there is still no proper way to ensure a positive integer is provided to a function and have the static type checks. I don't want to hack my own types or do runtime validation for something that is supposed to catch compile-time typos.

    I find guides such as https://www.convex.dev/typescript/typescript-101/basic-types-variables/typescript-integer-type where they seriously recommend to go the parseInt(x, 10) route again. I've used parseInt for input validation a decade ago, now it's 2025 and it is not solved?

    Is this some sick "embrace extend extinguish" by Microsoft? Am I too stupid to see the obvious solution?

  5. mkantor commented on Dec 9, 2025

    @mkantor
    Contributor

    Benjamin Flesch (@bf) if you want advice about a particular use case you could consider asking on the community Discord server, or maybe StackOverflow if the question seems like a good fit there.

  6. bf commented on Dec 9, 2025

    @bf
    Author

    Thanks for your kind replies. Will close this issue as I don't see any perspective how this will be fixed.

    There is an internal UINT8CLAMPED type in ECMAScript specs and it is also implemented in V8 engine. So it would be easy to add native support for it, saving 3 bytes for each number that is stored. See https://tc39.es/ecma262/multipage/indexed-collections.html#sec-uint8array and https://github.com/v8/v8/blob/5a615fbf808238714481b647676d65ba4ae62bbb/src/compiler/turboshaft/turbolev-graph-builder.cc#L5188

    There's even a recent ECMAscript proposal to add native Decimal type which also highlights current deficiencies of the language, see https://github.com/tc39/proposal-decimal

    Unfortunately the native Decimal proposal seems to have been shot down already, as discussed in tc39/proposal-decimal#192

    My expectation coming to typescript was that this whole class of Integer problems was finally solved, and I was a shocked that it still is such a problem.

    Seems like one has to fork v8 and deno to achieve this.

  7. MartinJohns commented on Dec 9, 2025

    @MartinJohns
    Contributor

    My expectation coming to typescript was that this whole class of Integer problems was finally solved, and I was a shocked that it still is such a problem.

    Seems like one has to fork v8 and deno to achieve this.

    That... Really makes no sense. V8/Deno are runtimes, TypeScript is a compile-time only language. Even if such an integer type would have been added in TypeScript, it would have NO effect on the runtime code.

  8. bf commented on Dec 10, 2025

    @bf
    Author

    My workaround for things like int8, uint8, int16 and uint16 in typescript is to explicitly define the type with numeric literals. Now I can use Byte and Byte[] types properly.

    let byteArrayInTypescript: Byte[] = [
      0,          // ok
      0xff,       // ok
      255,        // ok
      256,        // [ERROR]: Type '256' is not assignable to type 'Byte'.
      1000,       // [ERROR]: Type '1000' is not assignable to type 'Byte'.
      1.0,        // ok (lol)
      1.13,       // [ERROR]: Type '1.13' is not assignable to type 'Byte'.
      -1,         // [ERROR]: Type '-1' is not assignable to type 'Byte'.
      0xDD0000FF  // [ERROR]: Type '3707764991' is not assignable to type 'Byte'.
    ]

    It also works when passing an array directly to a function without explicitly specifying it as Byte[].

    function foobar (arr: Byte[]) {};
    
    foobar([1, 2, 3]) // works
    foobar([0x33, 0x44, 0x55]) // works
    foobar([0xDD0000FF])  // [ERROR]: Type '3707764991' is not assignable to type 'Byte'.

    Use command line to generate literal types:

    (echo -n "export type uint16 = "; (echo {1..65536}  | tr ' ' '|')) > ./types/uint16.ts

    Workaround for Byte type in typescript:

    export type Byte = 
      | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
      | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19
      | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29
      | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39
      | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49
      | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59
      | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69
      | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79
      | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89
      | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99
      | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109
      | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119
      | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129
      | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139
      | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149
      | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159
      | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169
      | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179
      | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189
      | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199
      | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209
      | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219
      | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229
      | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239
      | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249
      | 250 | 251 | 252 | 253 | 254 | 255;
  9. mkantor commented on Dec 10, 2025

    @mkantor
    Contributor

    Benjamin Flesch (@bf) you can also get cute and infer out of template literal types to generate such unions:

    type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
    type AsNumber<A extends string> = A extends `${infer B extends number}` ? B : never
    
    type Byte = AsNumber<
      // ^? type Byte = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |…
      | `${Digit}`
      | `${Exclude<Digit, 0>}${Digit}`
      | `1${Digit}${Digit}`
      | `2${0 | 1 | 2 | 3 | 4}${Digit}`
      | `25${0 | 1 | 2 | 3 | 4 | 5}`
    >

    But I think generally writing out the union is preferable. Even better than that is not leaning on giant unions in the first place (for multiple reasons, but a big one being they can significantly bog down type checking performance).

  10. bf commented on Dec 10, 2025

    @bf
    Author

    Thanks Matt Kantor (@mkantor), that's a clever approach. Unfortunately it does not work for 32 bit because - as expected - Expression produces a union type that is too complex to represent.. Was generating a multi-gigabyte uint32 type file already, but that approach obviously won't work...

    But I think generally writing out the union is preferable.

    Yes, simply using the workaround with Byte[] type to ensure that I don't get floats or negative numbers as function parameters is worth it. I persisted the types at https://github.com/bf/typescript-integer-types-uint8-int8-uint16-int16#

    Even better than that is not leaning on giant unions in the first place (for multiple reasons, but a big one being they can significantly bog down type checking performance).

    Yes, finally someone who understands me - how about we add/expose proper integer types that are already there in V8/ECMAScript specs. But I understand that you won't move into that direction. πŸ˜„

    How about this: TypeScript adds some sort of options for numeric parsing, so we don't need to abuse the expression parser. Why can't the expression parser have special case such as number<positive>? Or support for '+' operator when parsing digits, so that [0-9]+ works at least? This would be a typescript-only decision.

    Alternatively you should allow to define array lengths with the types, so that const foo: Uint8ClampedArray[1] fails when the array is empty or has more than one byte. If I think about it, maybe the array length specification is best.

  11. mkantor commented on Dec 10, 2025

    @mkantor
    Contributor

    But I understand that you won't move into that direction.

    I'm just a random TypeScript user, not a maintainer (though I've made some rather small contributions in this repository).

    How about this: …

    If you have other feature requests I think you ought to propose them in new issues. Be sure to search existing issues first though, like #41666, #43505, the related issues linked in comments above, etc.

    Alternatively you should allow to define array lengths with the types

    You can already do this with tuple types, though I think that only works for Arrays and not things like Uint8Array.


    I feel like you're probably already aware of bigint, but since it hasn't been mentioned yet in this thread I thought I ought to gesture towards it.

  12. MartinJohns commented on Dec 10, 2025

    @MartinJohns
    Contributor

    My workaround for things like int8, uint8, int16 and uint16 in typescript is to explicitly define the type with numeric literals. Now I can use Byte and Byte[] types properly.

    I get the workaround, also why you want this feature (I'd like it too). What confused me was the statement that you'd need to fork V8/Deno to achieve this, when the runtimes have nothing to do with the TypeScript types.

    proper integer types that are already there in V8/ECMAScript specs

    I'm not aware of any ECMAScript types beyond Number and BigInt.


    The issue that is being underestimated is the big can of worms that follows. Is an integer assignable to a number? Most would assume so. And an object with such a typed property? It introduces more unsoundness. You'd need to have integer be a subset of number, which is not something TypeScript currently has otherwise. Same deal with byte and any others. It's not as trivial to properly implement and support as you might think (feel free to prove me wrong :-P).

  13. locked as resolved and limited conversation to collaborators on Feb 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions