Skip to content

Instantly share code, notes, and snippets.

@erikyo
Last active May 8, 2024 20:00
Show Gist options
  • Save erikyo/3b0a67d55b97fbe8ec7dc8d5f310e9c4 to your computer and use it in GitHub Desktop.
Save erikyo/3b0a67d55b97fbe8ec7dc8d5f310e9c4 to your computer and use it in GitHub Desktop.
Strongly typed sprintf (allows placeholders and named placeholders)
type Specifiers = {
's': string,
'd': number,
'b': boolean,
'D': Date
};
type S = keyof Specifiers;
type ExtractNamedPlaceholders<T extends string> =
T extends `${infer Start}%(${infer Key})${infer Spec}${infer Rest}`
? Spec extends S
? { [K in Key]: Specifiers[Spec]} & ExtractNamedPlaceholders<Rest>
: never
: {};
type ExtractUnnamedPlaceholders<T extends string> =
T extends `${infer Start}%${infer Spec}${infer Rest}`
? Spec extends S
? [Specifiers[Spec], ...ExtractUnnamedPlaceholders<Rest>]
: never
: [];
type SprintfArgs<T extends string> =
ExtractUnnamedPlaceholders<T> extends never
? [values: ExtractNamedPlaceholders<T>]
: ExtractUnnamedPlaceholders<T>;
declare function sprintf<T extends string>(
format: T,
...values: SprintfArgs<T>
): string;
sprintf('this is a %s and it is %d years old, right?%b %D', 'erik', 20, true, new Date()); // ok, it checks for different types
sprintf('this is a %s and it is %b years old, right?%d %D', 'erik', 20, true, new Date()); // not ok, wrong type
sprintf('this is a %s and it is %b%b%b%byears old, right?%d %D', 'erik', 20, true, new Date()); // not ok, too many arguments
sprintf('Hello %s', 'John'); // ok
sprintf('Hello %s', 'John', 'Doe'); // not ok, too many arguments
sprintf('Hello %s', false); // not ok, wrong type
sprintf('Hello %s', false); // not ok, wrong type
sprintf("Ciao %(names)s", { names: "erik" }) // ok named placeholder
sprintf("Ciao %(names)s %(years)d", { names: "erik", years: 2 });// ok multiple named placeholder
sprintf("Ciao %(names)s %(years)d", { names: "erik", years: "2" });// not ok, wrong type
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment