mosya

mosya<TC> - Replace型を作って型で文字を置換しよう

この記事はmosya<TC>の問題の一つであるReplace型の解説になります。

問題

文字列Sに含まれる文字FromをToに一度だけ置き換える型Replace<S, From, To>を実装します。

例えば

type replaced = Replace<'types are fun!', 'fun', 'awesome'>; // expected to be 'types are awesome!'

受け取った文字列を Union 型に変換する型を実装します。

例えば以下のようなコードを満たすようにStringToUnion型を実装しましょう。

type Test = '123';
type Result = StringToUnion<Test>; // expected to be "1" | "2" | "3"

前提知識

この問題を解くにあたって型についての以下の知識を理解しておく必要があります。

  1. Conditional Typesを理解する
  2. inferを理解する
  3. Template Literal Typesを理解する

Conditional Typesを理解する

Conditional Typesは、条件によって型を変更することができる機能です。

例えば、以下のような型が考えられます。

type Foo<T> = T extends string ? string : number

この型は、Tstring型を継承している場合はstring型を、そうでない場合はnumber型を返します。

このように、extendsを使って条件を指定することで、型を変更することができます。

inferを理解する

inferは、型を推論することができる機能です。

例えば、以下のような型が考えられます。

type ArrayItem<T> = T extends (infer R)[] ? R : never

この型は、Tが配列の場合は、配列の中の型を返します。

以下のように使うことができます。

type Foo = ArrayItem<string[]> // string

この場合、infer Rにはstring[]型が当てはまるので、Rstring型に推論され、Rを返すのでstring型が返されます。
このように推論される型を取得するのにinferは役立ちます。

Template Literal Typesを理解する

Template Literal Typesは、文字列リテラルを使って型を作成することができる機能です。型を組み合わせて新しい文字列の型を作成することができます。

例えば、以下のような型が考えられます。

type Foo<T> = `${T} World`

type HelloWorld = Foo<'Hello'> // "Hello World"

このように、文字列リテラルを使って型を作成することができます。

解答例

以上の知識を使って、以下のように解答することができます。

type Replace<S extends string, From extends string, To extends string> = From extends '' ? S : S extends `${infer L}${From}${infer R}` ? `${L}${To}${R}` : S

まず、引数の、S, From, Toにはそれぞれ文字列型が入るので、S extends string, From extends string, To extends stringというように制約をつけます。

type Replace<S extends string, From extends string, To extends string> = ...

次に、Fromが空文字の場合は、Sをそのまま返すようにします。

type Replace<S extends string, From extends string, To extends string> = From extends '' ? S : ...

そうでない場合は、SFromを含んでいるかどうかを判定します。含んでいなければ、Sをそのまま返します。

type Replace<S extends string, From extends string, To extends string> = From extends '' ? S : S extends `${infer L}${From}${infer R}` ? ... : S

SFromを含んでいる場合は、Sの中からFromを取り除いて、Toを挿入します。

type Replace<S extends string, From extends string, To extends string> = From extends '' ? S : S extends `${infer L}${From}${infer R}` ? `${L}${To}${R}` : S

Authored by

筆者の写真

Godai@steelydylan

Webサービスを作るのが好きなWebエンジニア。子供が産まれたことをきっかけに独立し法人化。サービス開発が大好き。
好きな言語はTypeScript。

サイトの模写でプロを目指す
オンライン学習サービスを作りました!

詳しくはこちら
mosya

mosyaはオンラインでWeb制作を自学習できるサービスです。現役エンジニアが作成した豊富なカリキュラムに沿って学習を進めましょう。

© 2023 - mosya. All rights reserved.