テンプレートリテラル型でReplaceをする

22645 ワード

テンプレートリテラル型とは

type TemplateLiteralType = `dango-${ 'oishii' | 'kawaii' | 'daisuki' }`;
// dango-oishii | dango-kawaii | dango-daisuki

こんなやつです。

やってみる

こんなかんじです。

export type Replace<
  T extends string,
  S extends string,
  D extends string,
  A extends string = ''
> = T extends `${infer L}${S}${infer R}`
  ? Replace<R, S, D, `${A}${L}${D}`>
  : `${A}${T}`;

type Replaced = Replace<'dango-oishii', '-', '_'>;
// -> dango_ohagi

解説

まずは型引数から。
T には置き換える前の元の文字列を、S には置き換える部分の文字列を、D には置き換える後の文字列を指定します。
A は、置き換える前の文字列の前に付ける文字列を指定します。


T extends `${infer L}${S}${infer R}`

この部分は置き換える部分の位置を取得をするために、T が (文字列)S(文字列) という形になっているかを判断します。
もしそうなっているのであれば

? Replace<R, S, D, `${A}${L}${D}`>

ReplaceRを1つめの引数に、S, D の位置は変わらず、4つめの引数には ${A}${L}${D} を指定します。

export type Replace<
  T extends string  // R',
  S extends string, // S
  D extends string, // D
  A extends string = '' // `${A'}${L'}${D'}`
> = T extends `${infer L}${S}${infer R}` // <- これには当てはまらない
  ? Replace<R, S, D, `${A}${L}${D}`> 
  : `${A}${T}`; // <- ここが返される

そうすると、TR' という形になりR'Sがあれば繰り返し、なければ${infer L}${S}${infer R} には当てはまらなくなります。

: `${A}${T}`;

この部分のA${A'}${L'}${D'} が入り、T には R' が入ります。

dango-oishii-_に置き換える

type Replaced = Replace<'dango-oishii', '-', '_'>

export type Replace<
  T extends string  // 'dango-oishii'
  S extends string, // '-'
  D extends string, // '_'
  A extends string = ''
> = T extends `${infer L}${S}${infer R}`
//              'dango' + '-' + 'ohagi'
  ? Replace<R, S, D, `${A}${L}${D}`> // <- これが返される
// Replace<'ohagi', '-', '_', '' + 'dango' + '_' >
  : `${A}${T}`;

export type Replace<
  T extends string  // 'ohagi'
  S extends string, // '-'
  D extends string, // '_'
  A extends string = '' // '' + 'dango' + '_'
> = T extends `${infer L}${S}${infer R}`
//               'ohagi' -> よってここには当てはまらない
  ? Replace<R, S, D, `${A}${L}${D}`>
  : `${A}${T}`;
// '' + 'dango' + '_' + 'ohagi' -> dango_ohagi


dango_ohagi が返される。

おためし

マウスをホバーして確かめてみてくださいまし。