Examples Verified (100%)
マップ型
準備中
この機能は将来のリリースで計画されています。
マップ型を使用すると、プロパティを反復することで1つの型を別の型に変換できます。型のための「map」操作と考えてください—各プロパティに変換を適用することで、既存の型に基づいてプログラム的に新しい型を作成できます。
マップ型の理解
マップ型は型のキーを反復し、変換を適用して新しい型を作成します:
type MappedType<T> = {
[K in keyof T]: Transformation<T[K]>
}
基本構文
# Tのキーを反復
type ReadonlyType<T> = {
readonly [K in keyof T]: T[K]
}
# すべてのプロパティをオプショナルに
type OptionalType<T> = {
[K in keyof T]?: T[K]
}
# すべてのプロパティを必須に
type RequiredType<T> = {
[K in keyof T]-?: T[K]
}
keyof演算子
keyof演算子は型のすべてのキーをユニオンとして取得します:
type User = {
id: Integer,
name: String,
email: String
}
type UserKeys = keyof User # "id" | "name" | "email"
# マップ型で使用
type UserValues<T> = {
[K in keyof T]: T[K]
}
基本的なマップ型パターン
プロパティを読み取り専用に
# すべてのプロパティを読み取り専用に
type Readonly<T> = {
readonly [K in keyof T]: T[K]
}
type User = {
id: Integer,
name: String,
email: String
}
type ReadonlyUser = Readonly<User>
# {
# readonly id: Integer,
# readonly name: String,
# readonly email: String
# }
# 使用法
user: ReadonlyUser = { id: 1, name: "Alice", email: "alice@example.com" }
# user.name = "Bob" # エラー:読み取り専用プロパティに割り当てられない
プロパティをオプショナルに
# すべてのプロパティをオプショナルに
type Partial<T> = {
[K in keyof T]?: T[K]
}
type User = {
id: Integer,
name: String,
email: String
}
type PartialUser = Partial<User>
# {
# id?: Integer,
# name?: String,
# email?: String
# }
# 使用法 - すべてのプロパティがオプショナル
partial_user: PartialUser = { name: "Alice" } # OK
partial_user2: PartialUser = {} # OK
プロパティを必須に
# オプショナル修飾子を削除
type Required<T> = {
[K in keyof T]-?: T[K]
}
type UserUpdate = {
id?: Integer,
name?: String,
email?: String
}
type RequiredUserUpdate = Required<UserUpdate>
# {
# id: Integer,
# name: String,
# email: String
# }
プロパティ変換
プロパティの型を変換
# すべてのプロパティを配列に変換
type Arrayify<T> = {
[K in keyof T]: Array<T[K]>
}
type User = {
id: Integer,
name: String
}
type ArrayUser = Arrayify<User>
# {
# id: Array<Integer>,
# name: Array<String>
# }
# すべてのプロパティをPromiseに変換
type Promisify<T> = {
[K in keyof T]: Promise<T[K]>
}
# すべてのプロパティをnullableに変換
type Nullable<T> = {
[K in keyof T]: T[K] | nil
}
修飾子の追加または削除
# readonlyを追加
type AddReadonly<T> = {
+readonly [K in keyof T]: T[K]
}
# readonlyを削除
type RemoveReadonly<T> = {
-readonly [K in keyof T]: T[K]
}
# オプショナルに
type MakeOptional<T> = {
[K in keyof T]+?: T[K]
}
# 必須に
type MakeRequired<T> = {
[K in keyof T]-?: T[K]
}
キーのフィルタリング
特定のプロパティを選択
# 指定されたキーのみを選択
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
type User = {
id: Integer,
name: String,
email: String,
password: String
}
type PublicUser = Pick<User, "id" | "name">
# {
# id: Integer,
# name: String
# }
# 使用法
public_user: PublicUser = { id: 1, name: "Alice" }
特定のプロパティを除外
# 指定されたキーを除外
type Omit<T, K extends keyof T> = {
[P in Exclude<keyof T, K>]: T[P]
}
type User = {
id: Integer,
name: String,
email: String,
password: String
}
type UserWithoutPassword = Omit<User, "password">
# {
# id: Integer,
# name: String,
# email: String
# }
# 複数のプロパティを除外
type UserBasic = Omit<User, "password" | "email">
# {
# id: Integer,
# name: String
# }
条件付きマップ型
マップ型を条件型と組み合わせる:
# 条件に基づいてプロパティを読み取り専用に
type ConditionalReadonly<T> = {
readonly [K in keyof T]: T[K] extends Function ? T[K] : readonly T[K]
}
# 条件に基づいてプロパティをnullableに
type ConditionalNullable<T> = {
[K in keyof T]: T[K] extends String ? T[K] | nil : T[K]
}
# 関数を削除
type RemoveFunctions<T> = {
[K in keyof T as T[K] extends Function ? never : K]: T[K]
}
キーのリマッピング
マッピング中にプロパティキーを変換:
# すべてのキーにプレフィックスを追加
type Prefixed<T, Prefix extends String> = {
[K in keyof T as `${Prefix}${K}`]: T[K]
}
type User = {
id: Integer,
name: String
}
type PrefixedUser = Prefixed<User, "user_">
# {
# user_id: Integer,
# user_name: String
# }
# キーを大文字に変換
type Uppercased<T> = {
[K in keyof T as Uppercase<K>]: T[K]
}
# getterを追加
type WithGetters<T> = {
[K in keyof T as `get${Capitalize<K>}`]: () => T[K]
}
type User = { name: String, age: Integer }
type UserWithGetters = WithGetters<User>
# {
# getName: () => String,
# getAge: () => Integer
# }
実用的な例
DTOパターン
# データ転送オブジェクト - すべてのプロパティがオプショナルでnullableに
type DTO<T> = {
[K in keyof T]?: T[K] | nil
}
# APIレスポンス - データをラップ
type APIWrapper<T> = {
[K in keyof T]: {
value: T[K],
updated_at: Time
}
}
type User = {
name: String,
email: String
}
type UserDTO = DTO<User>
# {
# name?: String | nil,
# email?: String | nil
# }
type UserAPI = APIWrapper<User>
# {
# name: { value: String, updated_at: Time },
# email: { value: String, updated_at: Time }
# }
フォームハンドラ
# プロパティをフォームフィールドに変換
type FormFields<T> = {
[K in keyof T]: {
value: T[K],
error: String | nil,
touched: Boolean
}
}
# フォーム値のみ
type FormValues<T> = {
[K in keyof T]: T[K]
}
# フォームイベントハンドラ
type FormHandlers<T> = {
[K in keyof T as `on${Capitalize<K>}Change`]: (value: T[K]) => void
}
type LoginForm = {
username: String,
password: String
}
type LoginFields = FormFields<LoginForm>
# {
# username: { value: String, error: String | nil, touched: Boolean },
# password: { value: String, error: String | nil, touched: Boolean }
# }
type LoginHandlers = FormHandlers<LoginForm>
# {
# onUsernameChange: (value: String) => void,
# onPasswordChange: (value: String) => void
# }
データベースモデル
# モデルにタイムスタンプを追加
type WithTimestamps<T> = T & {
created_at: Time,
updated_at: Time
}
# 更新用にモデルを部分的に
type UpdateModel<T> = Partial<Omit<T, "id" | "created_at">>
# データベースメタデータを追加
type DBModel<T> = {
[K in keyof T]: {
value: T[K],
column_name: String,
dirty: Boolean
}
}
type User = {
id: Integer,
name: String,
email: String
}
type TimestampedUser = WithTimestamps<User>
# {
# id: Integer,
# name: String,
# email: String,
# created_at: Time,
# updated_at: Time
# }
type UserUpdate = UpdateModel<User>
# {
# name?: String,
# email?: String
# }
イベントハンドラ
# すべてのプロパティのイベントハンドラを作成
type EventHandlers<T> = {
[K in keyof T as `on${Capitalize<K>}Updated`]: (value: T[K]) => void
}
# すべてのプロパティのバリデータを作成
type Validators<T> = {
[K in keyof T]: (value: T[K]) => Boolean
}
# すべてのプロパティのシリアライザを作成
type Serializers<T> = {
[K in keyof T]: (value: T[K]) => String
}
type Product = {
name: String,
price: Float,
stock: Integer
}
type ProductHandlers = EventHandlers<Product>
# {
# onNameUpdated: (value: String) => void,
# onPriceUpdated: (value: Float) => void,
# onStockUpdated: (value: Integer) => void
# }
type ProductValidators = Validators<Product>
# {
# name: (value: String) => Boolean,
# price: (value: Float) => Boolean,
# stock: (value: Integer) => Boolean
# }
深いマップ型
ネストされたオブジェクトに再帰的にマッピングを適用:
# 深い読み取り専用
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends Hash<any, any>
? DeepReadonly<T[K]>
: T[K]
}
# 深い部分
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends Hash<any, any>
? DeepPartial<T[K]>
: T[K]
}
# 深い必須
type DeepRequired<T> = {
[K in keyof T]-?: T[K] extends Hash<any, any>
? DeepRequired<T[K]>
: T[K]
}
type NestedUser = {
profile: {
name: String,
settings: {
theme: String,
notifications: Boolean
}
}
}
type DeepReadonlyUser = DeepReadonly<NestedUser>
# すべてのネストされたプロパティが読み取り専用に
マップ型の結合
複数のマッピングを結合:
# 読み取り専用と部分
type ReadonlyPartial<T> = Readonly<Partial<T>>
# 読み取り専用と必須
type ReadonlyRequired<T> = Readonly<Required<T>>
# nullableと部分
type NullablePartial<T> = {
[K in keyof T]?: T[K] | nil
}
# Pickと部分
type PartialPick<T, K extends keyof T> = Partial<Pick<T, K>>
type User = {
id: Integer,
name: String,
email: String,
password: String
}
type SafeUserUpdate = ReadonlyPartial<Omit<User, "id">>
# {
# readonly name?: String,
# readonly email?: String,
# readonly password?: String
# }
型安全なビルダー
マップ型を使用して型安全なビルダーを作成:
# すべてのプロパティが設定されることを保証するビルダー
type Builder<T> = {
[K in keyof T as `with${Capitalize<K>}`]: (value: T[K]) => Builder<T>
} & {
build: () => T
}
# フルーエントAPI
type FluentAPI<T> = {
[K in keyof T]: (value: T[K]) => FluentAPI<T>
} & {
get: () => T
}
# 使用例
type User = {
name: String,
email: String,
age: Integer
}
type UserBuilder = Builder<User>
# {
# withName: (value: String) => UserBuilder,
# withEmail: (value: String) => UserBuilder,
# withAge: (value: Integer) => UserBuilder,
# build: () => User
# }
ベストプラクティス
1. 説明的な名前を使用
# 良い:明確な目的
type ReadonlyUser = Readonly<User>
type PartialUpdate = Partial<UserUpdate>
type PublicProfile = Pick<User, "id" | "name">
# あまり良くない:一般的な名前
type UserType1 = Readonly<User>
type UserType2 = Partial<UserUpdate>
2. 再利用可能なマップ型を作成
# 良い:再利用可能なユーティリティ
type WithTimestamps<T> = T & { created_at: Time, updated_at: Time }
type WithSoftDelete<T> = T & { deleted_at: Time | nil }
type WithMetadata<T> = T & { metadata: Hash<String, String> }
# 組み合わせる
type FullModel<T> = WithTimestamps<WithSoftDelete<WithMetadata<T>>>
3. 複雑なマッピングを文書化
# 良い:文書化されている
# すべてのプロパティを対応するgetterメソッドに変換
# 例:{ name: String } => { getName: () => String }
type ToGetters<T> = {
[K in keyof T as `get${Capitalize<K>}`]: () => T[K]
}
4. 条件型と組み合わせる
# 良い:スマートな変換
type SmartNullable<T> = {
[K in keyof T]: T[K] extends String | Integer
? T[K] | nil
: T[K]
}
一般的なパターン
リポジトリパターン
type Repository<T> = {
find_by_id: (id: Integer) => T | nil,
find_all: () => Array<T>,
save: (entity: T) => T,
update: (id: Integer, data: Partial<T>) => T | nil,
delete: (id: Integer) => Boolean
}
type CRUDHandlers<T> = {
create: (data: Omit<T, "id">) => T,
read: (id: Integer) => T | nil,
update: (id: Integer, data: Partial<T>) => T | nil,
delete: (id: Integer) => Boolean
}
状態管理
type State<T> = T
type Actions<T> = {
[K in keyof T as `set${Capitalize<K>}`]: (value: T[K]) => void
} & {
[K in keyof T as `get${Capitalize<K>}`]: () => T[K]
}
type Reducers<T> = {
[K in keyof T]: (state: T[K], action: any) => T[K]
}
制限事項
新しいプロパティを追加できない
# 元の型にないプロパティを追加できない
type Extended<T> = {
[K in keyof T]: T[K]
# new_property: String # エラー:マップ型で新しいプロパティを追加できない
}
# 代わりに交差を使用
type Extended<T> = T & { new_property: String }
キー型の制限
# キーはString | Symbol | Integerでなければならない
type ValidKeys = { [K in String]: any } # OK
type InvalidKeys = { [K in User]: any } # エラー:Userは有効なキー型ではない
次のステップ
マップ型を理解したので、次を探索してください: