type-challenges 中級編13: RequiredByKeys、Mutable、OmitByType
はじめに
GitHubのtype-challengesリポジトリは、TypeScript型システムの理解を深めるための型クイズ問題集です。高品質な型を作成することで、バグを減らし、プロジェクトの保守性を向上させることができます。今回も中級編から3つの問題に挑戦します。
1. RequiredByKeys
この課題では、ジェネリック型 T
と K
を受け取り、K
に指定されたプロパティのみを必須にする型を実装します。K
が指定されていない場合は、全てのプロパティを必須にします。
例
interface User {
name?: string
age?: number
address?: string
}
type UserRequiredName = RequiredByKeys<User, 'name'> // { name: string; age?: number; address?: string }
私の回答
type MergeObj<T> = {
[k in keyof T]: T[k]
}
type RequiredByKeys<T, K = keyof T> = MergeObj<{
[k in keyof T as k extends K ? never : k]: T[k]
} & {
[k in Exclude<keyof T, Exclude<keyof T, K>>]: T[k] extends infer P | undefined ? P : never
}>
解き方
- 前回解いた
PartialByKeys
と同様に、まずK
に指定されたプロパティを除外したオブジェクト型を生成し、次にK
に指定されたプロパティのみを必須にしたオブジェクト型を生成します。 - 最後にこれらのオブジェクト型をマージして、指定されたプロパティのみが必須になるようにします。
いやなんか無駄な記述多すぎる感、、
他の人の回答も見てみるとこんな回答がありました。
type RequiredByKeys<
T,
K extends keyof T = keyof T,
O = Omit<T, K> & { [P in K]-?: T[P] }
> = { [P in keyof O]: O[P] }
Omitのことを忘れていました、、
{ [P in K]-?: T[P] }
-?
でオプショナル消せるのかよ、、、、
2. Mutable
この課題では、オブジェクト型 T
の全てのプロパティをミュータブル(非読み取り専用)にする型を実装します。
例
interface Todo {
readonly title: string
readonly description: string
readonly completed: boolean
}
type MutableTodo = Mutable<Todo> // { title: string; description: string; completed: boolean; }
私の回答
type Mutable<T> = T extends Readonly<Array<unknown>> ?
T extends Readonly<[...infer A]> ? A : never
: {
[k in Exclude<keyof T, Exclude<T,''>>]: T[k]
}
これも無駄な記述多すぎる感がありますね、、
他の人の回答
type Mutable<T> = {
-readonly [K in keyof T]: T[K]
}
解き方
-
Mutable
は、プロパティを再定義して、readonly
修飾子を削除することで実装できます。 - 他の人の回答のように、
-readonly
修飾子を使用すると、全てのプロパティをミュータブルにすることができます。
いやreadonly
も-readonly
で消せるんかい!!
3. OmitByType
この課題では、ジェネリック型 T
から、型 U
に割り当て可能なプロパティを除外する型を実装します。
例
type OmitBoolean = OmitByType<{
name: string
count: number
isReadonly: boolean
isEnable: boolean
}, boolean> // { name: string; count: number }
私の回答
type OmitByType<T, U> = {
[k in keyof T as T[k] extends U ? never : k ]: T[k]
}
解き方
-
OmitByType
は、PickByType
の逆の操作を行います。 - プロパティの型が
U
に割り当て可能かどうかをチェックし、割り当て可能なプロパティを除外します。
これはいつものas
でどうにかなりますね。
まとめ
これらの課題を通じて、TypeScriptの型システムをより深く理解することができます。特にテンプレートリテラル型や再帰的な型の使用法について学ぶことで、より柔軟で強力な型を作成するスキルが身につきます。ぜひこれらの課題に挑戦して、型の理解を深めてください。