TL;DR
- テンプレートリテラル型を使うと、
stringより構造化された文字列をモデリングできる。 - ジェネリクスや型推論、条件型と組み合わせると、型どうしの関係や DSL のパーサを型で表現できる。
内容
構造化された文字列をモデリングする
テンプレートリテラル型のドメインは、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 の高度な機能を知らなくても、開発者体験が良くなるように努める。