TL;DR

内容

構造化された文字列をモデリングする

テンプレートリテラル型のドメインは、string と同じく無限である。ただし、テンプレートリテラル型を使うと、より構造化された文字列をモデリングできる。たとえば DOM 要素の data- で始まるプロパティは次のように書ける。

interface Checkbox {
  id: string
  checked: boolean
  [key: `data-${string}`]: unknown
}

型どうしの関係を表現する

テンプレートリテラル型をジェネリクスや型推論と組み合わせると、型どうしの関係を表現できる。例として querySelector の挙動を改善する。標準の型定義では、タグ名だけのセレクタなら HTMLImageElement | null が返るが、ID を付けると Element | null になってしまう。

// 修正前
const img = document.querySelector("img") // HTMLImageElement | null
const img = document.querySelector("img#spectacular-sunset") // Element | null

タグ名#ID の形のセレクタからタグ名を取り出すオーバーロードを足すと、ID 付きでも要素の型が残る。

type HTMLTag = keyof HTMLElementTagNameMap
type CSSSpecialChars = " " | ">" | "+" | "-" | "||" | ","
 
declare global {
  interface ParentNode {
    querySelector(selector: `${HTMLTag}#${string}${CSSSpecialChars}${string}`): Element | null
 
    querySelector<TagName extends HTMLTag>(
      selector: `${TagName}#${string}`,
    ): HTMLElementTagNameMap[TagName] | null
  }
}
 
const img = document.querySelector("img#spectacular-sunset") // HTMLImageElement | null

より厳密な CSS セレクタのパーサを作ることもできるが、ここでは最低限の実装にとどめている。

DSL のパーサを実装する

テンプレートリテラル型を条件型と組み合わせると、DSL のパーサを型で実装できる。次の例は、snake_case のオブジェクトのキーを camelCase に変換する型である。

type ToCamel<S extends string> = S extends `${infer Head}_${infer Tail}`
  ? `${Head}${Capitalize<ToCamel<Tail>>}`
  : S
 
type ObjectToCamel<T extends object> = {
  [K in keyof T as K extends string ? ToCamel<K> : K extends number ? K : never]: T[K]
}
 
function objectToCamel<T extends object>(obj: T): ObjectToCamel<T> {
  const out: any = {}
  for (const [k, v] of Object.entries(obj)) {
    out[camelCase(k)] = v
  }
  return out
}
 
const snake = { foo_bar: 12 }
const camel = objectToCamel(snake)
const val = camel.fooBar
//    ^? const camel: ObjectToCamel<{ foo_bar: number; }>
const val2 = camel.foo_bar
//                 ~~~~~~~ Property 'foo_bar' does not exist on type
//                         'ObjectToCamel<{ foo_bar: number; }>'.

この実装では camel の型の表示が理想的ではないが、これは改善できる(項目56 型の表示に配慮する)。

使うときの注意

テンプレートリテラル型を使うときは、利用者がテンプレートリテラル型や条件型、マップ型といった TypeScript の高度な機能を知らなくても、開発者体験が良くなるように努める。

キーワード