mrkeyoor.com_
Sat 08 Aug 21:02 UTC
npmUtilsupdated 08 Aug 2026

@schummar/icu-type-parser

@schummar/icu-type-parser is a type-only TypeScript utility that reads a literal ICU MessageFormat string at compile time and produces the object type for its arguments. `GetICUArgs<'Hello {name}'>` requires a `name` property, while number, plural, date, time, select, nested blocks, and tuples of messages receive more specific treatment. It does not parse, validate, or format anything when JavaScript runs; the published package exports only declarations.

Verdict

A sharp, tiny building block for authors of TypeScript-first i18n wrappers, not something most applications should install directly. If your messages are not literal types or you need runtime output, choose a real ICU parser or formatter.

API stability4/5Version 1.26.1 exposes only `GetICUArgs` and `GetICUArgsOptions`, so the public surface is unusually small and the package is past 1.0. The tradeoff is that observable behavior lives entirely in conditional types: a change to recursion, select-union merging, escape stripping, or the meaning of an option can alter assignment compatibility without producing any runtime failure for consumers to notice.
Docs1/5The published metadata calls it a TypeScript-powered ICU message parser, but the package has no dedicated README and the repository README documents `schummar-translate`. The generated declaration comments explain the final type and several internal helpers, yet users must read `extractICU.ts` to discover literal-type requirements, default `unknown` values, `ProvidedArgs`, select behavior, and the absence of a runtime export.
Maintenance4/5Version 1.26.1 was published on February 11, 2026, the monorepo was pushed on August 8, 2026, and GitHub reports only two open issues and pull requests. The package is released in step with the parent translation library. Confidence stops short of a top score because the package has no visible dedicated tests or documentation files in its own directory, only an entry point, package metadata, and build configuration.
Ecosystem3/5The package recorded 4,285,329 downloads for July 31 through August 6, 2026 and uses ordinary TypeScript types with no runtime dependencies. That makes it easy for libraries to consume without increasing application code size. Its direct community footprint is much smaller than the download count suggests: the parent repository has 136 stars, and the package does not integrate with runtime ICU parsers through an adapter or plugin API.

Use it if

  • You store ICU messages as TypeScript string literals and want missing interpolation values to fail type checking
  • You are building a typed translation wrapper and need to infer argument objects from each dictionary value
  • You want number, plural, date, time, and select arguments to map to application-defined TypeScript types
  • You need selected arguments, such as locale or tenant, to become optional because another layer supplies them
Skip it if

Setup reality

Install it as a development dependency because it has no runtime behavior: `npm install -D @schummar/icu-type-parser`. Import `GetICUArgs` with `import type`; a value import cannot work because version 1.26.1 exposes only `dist/index.d.ts` through its export map. The first surprise is that the default inferred property types are `unknown`. To get useful value checking, pass a second type argument that assigns your choices to `ICUArgument`, `ICUNumberArgument`, and `ICUDateArgument`. The second surprise is literal widening. A message declared with `let`, annotated as `string`, loaded from JSON, or returned by an untyped API loses its exact characters, so the compiler has nothing concrete to parse. Keep dictionaries `as const` and thread their literal types through generic helpers. `select` deserves tests in your own code: branches form a union, while an `other` branch intentionally permits arbitrary strings through a branded string type. `ProvidedArgs` is a union of argument names that should become optional because your wrapper supplies them. The parser understands nested braces and ICU apostrophe escapes through recursive conditional types, but it performs no runtime syntax check and ships no formatter. Pair it with `intl-messageformat` or a translation framework if strings must actually be rendered, and run type-check benchmarks before applying it to a very large catalog because all parsing work happens inside the TypeScript compiler.

Patterns

Install as a development dependencyinstall-type-only

npm install --save-dev @schummar/icu-type-parser

The package exposes declarations only; do not add it to code that must execute at runtime.

Infer a simple ICU argument objectinfer-simple-placeholder

import type {GetICUArgs} from '@schummar/icu-type-parser';

type WelcomeArgs = GetICUArgs<'Hello, {name}!'>;

const args: WelcomeArgs = {name: 'Ada'};

Without custom options, a plain placeholder is required but its value type is unknown.

Choose application-level value typesconfigure-argument-types

import type {GetICUArgsOptions} from '@schummar/icu-type-parser';

interface AppICUOptions extends GetICUArgsOptions {
  ICUArgument: string;
  ICUNumberArgument: number;
  ICUDateArgument: Date | number;
}

Use a reusable options type or plain placeholders remain unknown and number and date formats accept any value.

Require numbers for number formatstype-number-argument

type PriceArgs = GetICUArgs<
  'Total: {amount, number}',
  {ICUNumberArgument: number}
>;

const args: PriceArgs = {amount: 19.95};

The same ICUNumberArgument option is used for `number`, `plural`, and `selectordinal` formats.

Type date and time placeholderstype-date-and-time

type EventArgs = GetICUArgs<
  'Starts {when, date, short} at {when, time, short}',
  {ICUDateArgument: Date | number}
>;

const args: EventArgs = {when: new Date()};

Both `date` and `time` resolve through ICUDateArgument; the type parser does not format the Date.

Infer a plural counttype-plural-count

type ResultArgs = GetICUArgs<
  '{count, plural, =0 {No results} one {One result} other {# results}}',
  {ICUNumberArgument: number}
>;

const args: ResultArgs = {count: 3};

Plural and selectordinal selectors use ICUNumberArgument, but plural rules still require a runtime ICU formatter.

Infer named select choicestype-select-options

type LayoutArgs = GetICUArgs<
  '{layout, select, compact {Small} full {Large}}'
>;

const compact: LayoutArgs = {layout: 'compact'};
const full: LayoutArgs = {layout: 'full'};

Without an `other` branch, the selector is limited to the option labels found in the literal message.

Account for an ICU other branchallow-select-other

type StatusArgs = GetICUArgs<
  '{status, select, ready {Ready} other {Unknown}}'
>;

const known: StatusArgs = {status: 'ready'};
const future: StatusArgs = {status: 'queued'};

When `other` exists, the parser intentionally widens the selector to allow strings beyond the named options.

Collect arguments inside select branchesinfer-nested-arguments

type GreetingArgs = GetICUArgs<
  '{role, select, admin {Hello {adminName}} member {Hello {memberName}}}',
  {ICUArgument: string}
>;

const admin: GreetingArgs = {role: 'admin', adminName: 'Ada'};
const member: GreetingArgs = {role: 'member', memberName: 'Lin'};

Select branches form a union, so branch-specific placeholders can follow the selector as discriminated properties.

Infer arguments across several messagescombine-message-tuple

const messages = [
  'Hello {name}',
  'You have {count, number} alerts',
] as const;

type PageArgs = GetICUArgs<
  typeof messages,
  {ICUArgument: string; ICUNumberArgument: number}
>;

const args: PageArgs = {name: 'Ada', count: 2};

The input may be a readonly string tuple; `as const` preserves each message instead of widening the array to string[].

Mark wrapper-supplied arguments optionalmake-provided-args-optional

type RequestArgs = GetICUArgs<
  'Hello {name} from {tenant}',
  {ICUArgument: string; ProvidedArgs: 'tenant'}
>;

const callerArgs: RequestArgs = {name: 'Ada'};

ProvidedArgs accepts argument names and makes those properties optional; your runtime wrapper still has to supply them.

Ignore apostrophe-escaped ICU syntaxignore-escaped-braces

type HelpArgs = GetICUArgs<
  "Write '{name}' literally, then greet {user}",
  {ICUArgument: string}
>;

const args: HelpArgs = {user: 'Ada'};

ICU apostrophe escapes are stripped during type parsing, so `{name}` here is literal text rather than a required argument.

Alternatives

PackageRegistryPick it when
@formatjs/icu-messageformat-parsernpmYou need runtime ICU syntax parsing, validation, source locations, or an abstract syntax tree
intl-messageformatnpmYou need to compile and format ICU messages at runtime with locale-aware values
schummar-translatenpmYou want a complete typed translation library for React and Node rather than only argument inference