Examples Verified (100%)
ユーティリティ型
準備中
この機能は将来のリリースで計画されています。
ユーティリティ型は、一般的な型変換を実行する事前に構築されたジェネリック型です。型のための標準ライブラリのようなもので、日常的な型操作タスクのためのすぐに使えるソリューションを提供します。これらのユーティリティを理解すると、T-Rubyコードがより簡潔で表現力豊かになります。
プロパティ修飾子
Partial<T>
型のすべてのプロパティをオプショナルにします:
type Partial<T> = {
[K in keyof T]?: T[K]
}
type User = {
id: Integer,
name: String,
email: String,
age: Integer
}
type PartialUser = Partial<User>
# {
# id?: Integer,
# name?: String,
# email?: String,
# age?: Integer
# }
# 使用法 - update関数はよくPartialを使用
def update_user(id: Integer, updates: Partial<User>): User
user = find_user(id)
# 提供された更新のみを適用
user.name = updates[:name] if updates[:name]
user.email = updates[:email] if updates[:email]
user.age = updates[:age] if updates[:age]
user
end
# プロパティの任意のサブセットを提供可能
update_user(1, { name: "Alice" })
update_user(1, { name: "Bob", email: "bob@example.com" })
update_user(1, {}) # 有効、更新なし
Required<T>
すべてのプロパティを必須にします(オプショナル性を削除):
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
# }
# 使用法 - すべてのフィールドが存在することを確認
def create_user(data: Required<UserUpdate>): User
# すべてのフィールドが存在することを保証
User.new(data[:id], data[:name], data[:email])
end
Readonly<T>
すべてのプロパティを読み取り専用にします:
type Readonly<T> = {
readonly [K in keyof T]: T[K]
}
type User = {
id: Integer,
name: String,
email: String
}
type ReadonlyUser = Readonly<User>
# 使用法 - 変更を防止
def display_user(user: ReadonlyUser): void
puts "User: #{user.name}"
# user.name = "Changed" # エラー:読み取り専用プロパティを変更できない
end
# 一般的なパターン:設定を固定
type Config = Readonly<{
api_url: String,
timeout: Integer,
max_retries: Integer
}>
config: Config = {
api_url: "https://api.example.com",
timeout: 30,
max_retries: 3
}
プロパティ選択
Pick<T, K>
別の型から特定のプロパティを選んで型を作成:
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
type User = {
id: Integer,
name: String,
email: String,
password: String,
created_at: Time,
updated_at: Time
}
# 公開フィールドのみを選択
type PublicUser = Pick<User, "id" | "name" | "email">
# {
# id: Integer,
# name: String,
# email: String
# }
# 認証フィールドを選択
type AuthUser = Pick<User, "id" | "email" | "password">
# {
# id: Integer,
# email: String,
# password: String
# }
# 使用法
def get_public_user(user: User): PublicUser
{
id: user.id,
name: user.name,
email: user.email
}
end
Omit<T, K>
別の型から特定のプロパティを除外して型を作成:
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,
created_at: Time,
updated_at: Time
}
# 機密データを除外
type SafeUser = Omit<User, "password">
# {
# id: Integer,
# name: String,
# email: String,
# created_at: Time,
# updated_at: Time
# }
# 作成用に生成フィールドを除外
type UserInput = Omit<User, "id" | "created_at" | "updated_at">
# {
# name: String,
# email: String,
# password: String
# }
# 使用法
def create_user(input: UserInput): User
User.new(
id: generate_id(),
name: input[:name],
email: input[:email],
password: hash_password(input[:password]),
created_at: Time.now,
updated_at: Time.now
)
end
ユニオンと交差演算
Exclude<T, U>
ユニオンから型を除外:
type Exclude<T, U> = T extends U ? never : T
type AllTypes = String | Integer | Float | Boolean
type NumericOnly = Exclude<AllTypes, String | Boolean>
# Integer | Float
type Status = "pending" | "approved" | "rejected" | "cancelled"
type ActiveStatus = Exclude<Status, "cancelled">
# "pending" | "approved" | "rejected"
# 使用法
def process_active_order(status: ActiveStatus): void
case status
when "pending"
puts "Processing pending order"
when "approved"
puts "Shipping approved order"
when "rejected"
puts "Handling rejected order"
# "cancelled"はここでは不可能
end
end
Extract<T, U>
ユニオンから型を抽出:
type Extract<T, U> = T extends U ? T : never
type AllTypes = String | Integer | Float | Boolean
type NumericOnly = Extract<AllTypes, Integer | Float>
# Integer | Float
type Status = "pending" | "approved" | "rejected" | "cancelled"
type FinalStatus = Extract<Status, "approved" | "rejected" | "cancelled">
# "approved" | "rejected" | "cancelled"
# 使用法
def finalize_order(status: FinalStatus): void
# 最終ステータスのみ許可
puts "Order is in final state: #{status}"
end
NonNullable<T>
型からnilを削除:
type NonNullable<T> = T extends nil ? never : T
type MaybeString = String | nil
type DefiniteString = NonNullable<MaybeString>
# String
type MixedTypes = String | Integer | nil | Float | nil
type WithoutNil = NonNullable<MixedTypes>
# String | Integer | Float
# 使用法
def process_value<T>(value: T | nil): NonNullable<T>
raise "Value cannot be nil" if value.nil?
value # 型がTにナローイング(nilなし)
end
関数型
ReturnType<T>
関数型の戻り値の型を抽出:
type ReturnType<T> = T extends Proc<any, infer R> ? R : never
type GetUserFn = Proc<Integer, User>
type UserType = ReturnType<GetUserFn>
# User
type CalculateFn = Proc<Integer, Integer, Float>
type ResultType = ReturnType<CalculateFn>
# Float
# 使用法 - 関数から戻り値の型を推論
def wrap_function<F>(fn: F): Proc<any, ReturnType<F>>
->(args: any): ReturnType<F> {
result = fn.call(args)
puts "Function returned: #{result}"
result
}
end
Parameters<T>
関数型からパラメータ型を抽出:
type Parameters<T> = T extends Proc<infer P, any> ? P : never
type GetUserFn = Proc<Integer, User>
type GetUserParams = Parameters<GetUserFn>
# Integer
type CreateUserFn = Proc<String, String, Integer, User>
type CreateUserParams = Parameters<CreateUserFn>
# [String, String, Integer]
# 使用法
def call_with_logging<F>(fn: F, ...args: Parameters<F>): ReturnType<F>
puts "Calling function with args: #{args}"
result = fn.call(*args)
puts "Function returned: #{result}"
result
end
レコード型
Record<K, V>
キー型がKで値型がVの型を作成:
type Record<K extends String | Symbol | Integer, V> = {
[P in K]: V
}
# 文字列キー、整数値
type StringToInt = Record<String, Integer>
# { [key: String]: Integer }
# 特定の文字列リテラルキー
type StatusMap = Record<"pending" | "approved" | "rejected", Boolean>
# {
# pending: Boolean,
# approved: Boolean,
# rejected: Boolean
# }
# 使用例
status_flags: StatusMap = {
pending: true,
approved: false,
rejected: false
}
# ユーザーIDからUserへのマッピング
user_cache: Record<Integer, User> = {
1 => User.new(1, "Alice"),
2 => User.new(2, "Bob")
}
# 環境ごとの設定
configs: Record<"development" | "staging" | "production", Config> = {
development: dev_config,
staging: staging_config,
production: prod_config
}
配列とタプルユーティリティ
ArrayElement<T>
配列から要素型を抽出:
type ArrayElement<T> = T extends Array<infer E> ? E : never
type StringArray = Array<String>
type StringElement = ArrayElement<StringArray>
# String
type UserArray = Array<User>
type UserElement = ArrayElement<UserArray>
# User
# 使用法
def first_element<T>(arr: Array<T>): ArrayElement<Array<T>> | nil
arr.first
end
ReadonlyArray<T>
要素を変更できない配列:
type ReadonlyArray<T> = readonly Array<T>
# 使用法
def process_items(items: ReadonlyArray<String>): void
items.each { |item| puts item }
# items.push("new") # エラー:読み取り専用配列を変更できない
# items[0] = "changed" # エラー:読み取り専用配列を変更できない
end
# 定数に便利
ALLOWED_STATUSES: ReadonlyArray<String> = ["pending", "approved", "rejected"]
実用的なユーティリティ型
DeepPartial<T>
すべてのプロパティとネストされたプロパティをオプショナルに:
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends Hash<any, any>
? DeepPartial<T[K]>
: T[K]
}
type User = {
id: Integer,
profile: {
name: String,
email: String,
settings: {
theme: String,
notifications: Boolean
}
}
}
type DeepPartialUser = DeepPartial<User>
# すべてのレベルのすべてのプロパティがオプショナル
# 使用法 - 深い更新
def deep_update_user(id: Integer, updates: DeepPartial<User>): User
user = find_user(id)
# 任意のネストされたプロパティを更新可能
user.profile.name = updates[:profile][:name] if updates[:profile]&.[](:name)
user.profile.settings.theme = updates[:profile][:settings][:theme] if updates[:profile][:settings]&.[](:theme)
user
end
DeepReadonly<T>
すべてのプロパティとネストされたプロパティを読み取り専用に:
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends Hash<any, any>
? DeepReadonly<T[K]>
: T[K]
}
type Config = {
app: {
name: String,
version: String,
features: {
auth: Boolean,
api: Boolean
}
}
}
type ImmutableConfig = DeepReadonly<Config>
# すべてのネストされたプロパティが読み取り専用
config: ImmutableConfig = load_config()
# config.app.name = "New" # エラー:読み取り専用
# config.app.features.auth = false # エラー:読み取り専用
Mutable<T>
読み取り専用修飾子を削除:
type Mutable<T> = {
-readonly [K in keyof T]: T[K]
}
type ReadonlyUser = {
readonly id: Integer,
readonly name: String
}
type MutableUser = Mutable<ReadonlyUser>
# {
# id: Integer,
# name: String
# }
# 使用法 - 変更可能なコピーを作成
def clone_user(user: ReadonlyUser): MutableUser
{
id: user.id,
name: user.name
}
end
合成ユーティリティ
Merge<T, U>
2つの型をマージし、UのプロパティがTのプロパティを上書き:
type Merge<T, U> = Omit<T, keyof U> & U
type User = {
id: Integer,
name: String,
email: String
}
type UserUpdate = {
email: String | nil, # nullを許可
updated_at: Time # 新しいプロパティ
}
type MergedUser = Merge<User, UserUpdate>
# {
# id: Integer,
# name: String,
# email: String | nil, # 上書きされた
# updated_at: Time # 追加された
# }
Intersection<T, U>
両方の型に存在するプロパティを取得:
type Intersection<T, U> = Pick<T, Extract<keyof T, keyof U>>
type User = {
id: Integer,
name: String,
email: String
}
type Person = {
name: String,
email: String,
age: Integer
}
type Common = Intersection<User, Person>
# {
# name: String,
# email: String
# }
Difference<T, U>
Tにあるがにないプロパティを取得:
type Difference<T, U> = Omit<T, keyof U>
type User = {
id: Integer,
name: String,
email: String,
password: String
}
type PublicFields = {
id: Integer,
name: String
}
type PrivateFields = Difference<User, PublicFields>
# {
# email: String,
# password: String
# }
条件付きユーティリティ
If<Condition, Then, Else>
型レベルのif-else:
type If<Condition extends Boolean, Then, Else> =
Condition extends true ? Then : Else
# 使用法
type IsProduction<Env> = If<
Env extends "production",
{ debug: false, logging: :error },
{ debug: true, logging: :debug }
>
type ProdConfig = IsProduction<"production">
# { debug: false, logging: :error }
type DevConfig = IsProduction<"development">
# { debug: true, logging: :debug }
Nullable<T>
型をnullableに:
type Nullable<T> = T | nil
type User = { id: Integer, name: String }
type MaybeUser = Nullable<User>
# User | nil
# 関数で使用
def find_user(id: Integer): Nullable<User>
# 見つからない場合はnilを返す可能性
end
Promisify<T>
戻り値の型をPromiseでラップ(非同期操作用):
type Promisify<T> = {
[K in keyof T]: T[K] extends Proc<infer Args, infer R>
? Proc<Args, Promise<R>>
: T[K]
}
type UserService = {
find: Proc<Integer, User>,
create: Proc<String, String, User>,
delete: Proc<Integer, Boolean>
}
type AsyncUserService = Promisify<UserService>
# {
# find: Proc<Integer, Promise<User>>,
# create: Proc<String, String, Promise<User>>,
# delete: Proc<Integer, Promise<Boolean>>
# }
実世界の例
APIレスポンス型
type APIResponse<T> = {
success: true,
data: T
} | {
success: false,
error: String,
code: Integer
}
# 使用法
def fetch_user(id: Integer): APIResponse<User>
begin
user = find_user(id)
{ success: true, data: user }
rescue => e
{ success: false, error: e.message, code: 500 }
end
end
# レスポンスを処理
response = fetch_user(1)
if response[:success]
user = response[:data] # 型はUser
puts user.name
else
error = response[:error] # 型はString
puts "Error: #{error}"
end
フォーム状態管理
type FormState<T> = {
values: T,
errors: Partial<Record<keyof T, String>>,
touched: Partial<Record<keyof T, Boolean>>,
dirty: Boolean,
valid: Boolean
}
type LoginForm = {
username: String,
password: String
}
type LoginFormState = FormState<LoginForm>
# {
# values: { username: String, password: String },
# errors: { username?: String, password?: String },
# touched: { username?: Boolean, password?: Boolean },
# dirty: Boolean,
# valid: Boolean
# }
リポジトリパターン
type Repository<T> = {
find: Proc<Integer, Nullable<T>>,
find_all: Proc<Array<T>>,
create: Proc<Omit<T, "id">, T>,
update: Proc<Integer, Partial<T>, Nullable<T>>,
delete: Proc<Integer, Boolean>
}
type User = {
id: Integer,
name: String,
email: String
}
# 自動的に型付けされたリポジトリ
user_repository: Repository<User> = create_repository(User)
# 適切な型での使用
new_user = user_repository.create({ name: "Alice", email: "alice@example.com" })
updated = user_repository.update(1, { name: "Alice Smith" })
ベストプラクティス
1. ユーティリティを構成
# 良い:ユーティリティから複雑な型を構築
type SafeUserUpdate = Partial<Omit<Required<User>, "id" | "created_at">>
# あまり良くない:ユーティリティがあるときにカスタムマップ型
type SafeUserUpdate = {
[K in Exclude<keyof User, "id" | "created_at">]?: User[K]
}
2. ドメイン固有のユーティリティを作成
# 良い:ドメイン用のカスタムユーティリティ
type Entity<T> = T & { id: Integer }
type Timestamped<T> = T & { created_at: Time, updated_at: Time }
type SoftDeletable<T> = T & { deleted_at: Nullable<Time> }
type FullEntity<T> = Entity<Timestamped<SoftDeletable<T>>>
# 使用法
type User = FullEntity<{ name: String, email: String }>
3. 複雑なユーティリティを文書化
# 良い:明確な文書化
# 任意のモデルのための型安全なフォーム状態を作成
# 検証エラーとtouched状態の追跡を含む
type FormState<T> = {
values: T,
errors: Partial<Record<keyof T, String>>,
touched: Partial<Record<keyof T, Boolean>>,
submitting: Boolean
}
次のステップ
ユーティリティ型を理解したので、次のことができます: