type-challenges 中級編4: Type Lookup, Trim Left, Trim
はじめに
GitHubのtype-challengesリポジトリは、TypeScript型システムの理解を深めるための型クイズ問題集です。高品質な型を作成することで、バグを減らし、プロジェクトの保守性を向上させることができます。今回は中級編から3つの問題に挑戦します。
1. Type Lookup
Union型から特定の型を属性を使って取得する汎用的な LookUp<U, T>
を実装してください。
例
interface Cat {
type: 'cat'
breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
}
interface Dog {
type: 'dog'
breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
color: 'brown' | 'white' | 'black'
}
type MyDog = LookUp<Cat | Dog, 'dog'> // expected to be `Dog`
私の回答
type LookUp<U, T> = U extends { type: T } ? U : never
解き方
-
U extends { type: T }
という条件型を使い、Union型U
の中から特定の型T
を持つものを推定します。 - もし
U
が{ type: T }
を満たす場合、その型U
を返します。 - そうでない場合は
never
を返します。
inferが使えれば簡単。
2. Trim Left
文字列を受け取り、先頭の空白を削除した新しい文字列を返す汎用的な TrimLeft<T>
を実装してください。
例
type trimed = TrimLeft<' Hello World '> // expected to be 'Hello World '
私の回答
type TrimLeft<S extends string> = S extends ` ${infer T}` ? TrimLeft<T>:
S extends `\n\t${infer X}` ? TrimLeft<X> : S
解き方
-
S extends
${infer T}`` という条件型を使い、文字列S
の先頭の空白を削除します。 - もし
S
が先頭に空白を含む場合、その空白を削除して再帰的にTrimLeft<T>
を適用します。 - そうでない場合はそのまま
S
を返します。
型でこんな文字列操作もできることは覚えておきましょう。(実務で使うかは謎ですが、、)
3. Trim
文字列を受け取り、両端の空白を削除した新しい文字列を返す汎用的な Trim<T>
を実装してください。
例
type trimed = Trim<' Hello World '> // expected to be 'Hello World'
私の回答
type Trim<S extends string> = S extends ` ${infer T1}` ? Trim<T1>:
S extends `\n\t${infer T2}` ? Trim<T2> :
S extends `${infer T3} ` ? Trim<T3>:
S extends `${infer T4}\t` ? Trim<T4> :
S
解き方
-
S extends
${infer T1}という条件型を使い、文字列
S` の先頭の空白を削除します。 -
S extends
\n\t${infer T2}という条件型を使い、文字列
S` の先頭の改行、タブを削除します。 -
S extends
${infer T}という条件型を使い、文字列
S` の末尾の空白を削除します。 -
S extends
${infer T2}\n\tという条件型を使い、文字列
S` の末尾の改行、タブを削除します。 - これを再帰的に適用することで、両端の空白、タブを削除します。
さっきと同じ。
なんか無駄多いな、と思い他の人の実装を見てみたところ
type Space = ' ' | '\t' | '\n';
type Trim<S extends string> = S extends `${Space}${infer T}` | `${infer T}${Space}` ? Trim<T> : S;
みたいな書き方がいいみたいですね。
まとめ
このように、TypeScriptの型システムを理解し、実際に型を実装することで、より安全で保守性の高いコードを書くことができます。ぜひこれらの課題を解いて、型力UPしてみてください。