TypeScriptTips

Make bugs impossible.
One TypeScript tip at a time.

I like the tricks :) – Kasia

Type-safe string templating

Let the compiler ensure that every placeholder in the template string is specified in the values to replace.

const replace =
  (template: string, values: Record<string, string>): string =>
    template.replace(/{{(\w+)}}/g, (_, capture) => values[capture])

replace("hello, {{name}}", {}) // ⛔️ "hello, undefined"
type Values<Template> = Template extends
  `${infer _Ignore}{{${infer Capture}}}${infer Rest}` ?
    Record<Capture, string> | Values<Rest> :
    never

const replace =
  <Template extends string>
    (template: Template, values: Values<Template>): string =>
      template.replace(/{{(\w+)}}/g, (_, capture) => values[capture])

replace("hello, {{name}}", {name: "mary"}) // ✅ "hello, mary"
replace("hello, {{name}}", {}) // ✅ Does not compile
replace("hello, {{name}}", { namme: "mary"} // ✅ Does not compile