Skip to content

Instantly share code, notes, and snippets.

@gaearon
Last active September 3, 2025 00:59
Show Gist options
  • Save gaearon/966446d27de88ba4d4806a9a5b291d0a to your computer and use it in GitHub Desktop.
Save gaearon/966446d27de88ba4d4806a9a5b291d0a to your computer and use it in GitHub Desktop.
type UnitInterval = number & { __brand: 'UnitInterval' };
function makeUnitInterval(x: number): UnitInterval {
if (x >= 0 && x <= 1) {
return x as UnitInterval;
}
throw RangeError('x must be between 0 and 1');
}
function someFunction(x: UnitInterval): number {
return x ** 2;
}
someFunction(0.5) // Doesn't typecheck
someFunction(1.2) // Doesn't typecheck
someFunction(-1) // Doesn't typecheck
someFunction(makeUnitInterval(0.5)) // Typechecks
someFunction(makeUnitInterval(1.2)) // Also typechecks :(
someFunction(makeUnitInterval(-1)) // Also typechecks :(
@mkantor
Copy link

mkantor commented Aug 17, 2025

It's debatable whether this is "better", but you could also use what is sometimes called a "validator type":

type MustBeBetweenZeroAndOne<N extends number> =
  N extends 0 | 1 ? N
  : `${N}` extends `0.${string}` ? N
  : never

function someFunction<N extends number>(x: MustBeBetweenZeroAndOne<N>): number {
  return x ** 2;
}

// These typecheck:
someFunction(0)
someFunction(0.5)
const x = 0.999
someFunction(x)
someFunction(1.0)

// These don't typecheck:
someFunction(-0.1)
someFunction(1.5)
someFunction(Infinity)
someFunction(0.1 + 0.1) // :( the type system doesn't do arithmetic, see <https://github.com/microsoft/TypeScript/issues/26382>

@20jasper
Copy link

20jasper commented Sep 3, 2025

For funsies, you could theoretically add every representable literal to a union and type check that. TypeScript seems to give up after 1x10^-16 and convert to an integer. JS can handle even smaller positive integers, though that would balloon our range to 2^-1024

Assuming typechecking floats literals is about the same effort as typechecking integer literals, it would theoretically take around 14 yottabytes of RAM to typecheck

The numbers are extrapolated from an article I wrote about typechecking a union of 1.8x10^16, and dividing that number by 1.8 to handle our union of size 10^16
https://jacobasper.com/blog/it-takes-26-yottabytes-of-ram-to-typecheck-a-union-of-safe-integers/

So long story short, the UnitInterval dream can be real with enough RAM 😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment