Skip to content

Regex \d+ infers ${number}, which rejects valid digit strings longer than ~309 digits #1663

Description

@danfry1

Report a bug

🔎 Search Terms

regex, \d+, ${number}, Infinity, long digit string, arkregex, never incorrect

🧩 Context

  • ArkType version: 2.2.5 (arkregex 0.0.10)
  • TypeScript version (5.1+): 5.1.6, 5.3.3, 5.4.5, 5.9.3, 7.0.2, 7.1.0-dev.20260926.1 (same result on each; ${number} rejecting non-finite strings is standard TypeScript behavior, so this isn't version-specific)
  • Other context you think may be relevant (JS flavor, OS, etc.): Node 24, macOS

🧑‍💻 Repro

Playground Link: N/A. The playground doesn't resolve regex types yet (regex() is any there, see #1529, and type("/^\\d+$/") reports "not allowed here"), so the repro below was verified with a local install of arktype@2.2.5 instead.

import { type } from "arktype"

const Digits = type("/^\\d+$/")
type Digits = typeof Digits.infer // `${number}`

// "1" followed by 309 zeros
const big =
	"1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

Digits(big) // passes validation and returns the string

const x: Digits = big
// ❌ Type '"1000…0"' is not assignable to type '`${number}`'

A string is only a ${number} if Number(s) is finite, and Number(big) is Infinity. So any digit run above ~1.8e308 passes validation but isn't assignable to the inferred type. The same happens with regex("^\\d+$"), with \d*, and with digit runs inside larger patterns such as ^v\d+\.\d+$ and the semver core regex.

It's an edge case, but it breaks the guarantee from the ArkRegex announcement that inferred types are "at worst imprecise and never incorrect": validated data ends up typed as something it isn't.

A sound replacement for \d+ that works on every supported TS version (5.1+):

type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
type DigitsPlus = Lowercase<string> &
	Uppercase<string> &
	`${Digit}${string}` &
	`${string}${Digit}`

It has no length limit and still rejects things like "1e5", "0x1f" and " 1". I'm also opening a feature request with a more precise version for TS 5.3+.

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions