type-challenges 中級編11: Drop Char、MinusOne、PickByType
はじめに
GitHubのtype-challengesリポジトリは、TypeScript型システムの理解を深めるための型クイズ問題集です。高品質な型を作成することで、バグを減らし、プロジェクトの保守性を向上させることができます。今回は中級編から3つの問題に挑戦します。
1. Drop Char
この課題では、文字列から指定された文字を削除する型を実装します。
例
type Butterfly = DropChar<' b u t t e r f l y ! ', ' '> // 'butterfly!'
私の回答
type DropChar<S, C extends string> =
S extends `${infer L}${C}${infer R}` ? `${L}${DropChar<R,C>}` : S
解き方
- 文字列型
S
を分解し、指定された文字C
が見つかった場合にその文字を取り除いて再帰的に処理します。 - 基本的にテンプレートリテラル型を使用して文字列を分割し、不要な文字を削除します。
いつもの文字列操作ですね
${infer L}${C}${infer R}
で取り出すのを覚えておけば簡単!
2. MinusOne
この課題では、数値型を1減らす型を実装します。
例
type Zero = MinusOne<1> // 0
type FiftyFour = MinusOne<55> // 54
今回は答え見ちゃいました(難しくない..?)
type Pop<T extends any[]> = T extends [...infer Head, any] ? Head : never;
type MinusOne<T extends number, A extends any[] = []> = A['length'] extends T
? Pop<A>['length']
: MinusOne<T, [...A, 0]>
解き方
- 数値
T
を配列の長さとして取り扱い、1減らす操作を再帰的に行います。 -
Pop
型を使用して配列の最後の要素を取り除きます。 - 再帰的に配列を拡張していき、配列の長さが
T
に達した時点で1減らした結果を返します。
文字列リテラル型の操作、オブジェクト、配列操作、Unionの型操作はできるが数値をいじるのはかなり難しい
一度配列に直さないといけない、、
3. PickByType
この課題では、指定された型 U
に割り当て可能なプロパティのみをピックする型を実装します。
例
type OnlyBoolean = PickByType<{
name: string;
count: number;
isReadonly: boolean;
isEnable: boolean;
}, boolean>; // { isReadonly: boolean; isEnable: boolean; }
私の回答
type PickByType<T, U> = {
[K in keyof T as U extends T[K] ? K : never]: T[K];
}
解き方
- オブジェクト型
T
の各プロパティK
が型U
に割り当て可能かをチェックします。 - 割り当て可能なプロパティのみを新しいオブジェクト型に含めます。
as でオブジェクトのプロパティをいじる事ができればできる!(as never
でプロパティを削除することもできる)
まとめ
これらの課題を通じて、TypeScriptの型システムをより深く理解することができます。特にテンプレートリテラル型や再帰的な型の使用法について学ぶことで、より柔軟で強力な型を作成するスキルが身につきます。ぜひこれらの課題に挑戦して、型の理解を深めてください。