create-objects-all-at-once について
オブジェクトのプロパティを後から追加せず、一度に完成させるTypeScript向けスキルです。オブジェクトリテラルとスプレッド構文を使い、型アサーションに頼らず型チェックを保つ方法を説明します。
主な機能
- 01全プロパティを一度に持つオブジェクトの作成
- 02スプレッド構文によるオブジェクト結合
- 03条件付きプロパティの安全な追加
- 04関数型の構文によるデータ変換
- 05安全でない型アサーションの回避
活用シーン
- 01型付き設定オブジェクトの構築
- 02複数ソースのプロパティ結合
- 03条件に応じたオプション項目の追加
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill create-objects-all-at-onceインストール後、Claude Code で/create-objects-all-at-onceを入力して読み込まれたか確認します。
詳細
オブジェクトは一度に作成する
概要
オブジェクトは少しずつではなく、1つの文で構築します。
TypeScript は、オブジェクトの作成時にその型を推論します。後からプロパティを追加すると、型エラーが発生します。オブジェクトリテラルとスプレッド構文を使って、オブジェクトを一度に完成させましょう。
このスキルを使う場面
- オブジェクトを段階的に作成するとき
- オブジェクトの作成後にプロパティを追加するとき
- 「プロパティが存在しない」というエラーが出るとき
- 複数のオブジェクトをマージするとき
絶対的なルール
Define all properties in a single object literal.
Use spread syntax (...) to combine objects.
覚えておきましょう:
- オブジェクトの型は作成時に固定されます
- 型アサーションでは解決できません
- オブジェクトスプレッドを使うと型安全性を保てます
- スプレッドのたびに新しい変数を作成します
検出:段階的な構築では失敗する
const pt = {};
pt.x = 3;
// ~ Property 'x' does not exist on type '{}'
pt.y = 4;
// ~ Property 'y' does not exist on type '{}'
TypeScript は pt の型を {} と推論するため、プロパティを追加できません。
型アサーションによる「修正」(問題あり)
interface Point { x: number; y: number; }
const pt = {} as Point; // Assertion silences errors
pt.x = 3;
pt.y = 4;
// But: TypeScript won't check you assigned all properties!
const pt2 = {} as Point; // No error, but x and y are undefined
型アサーションは安全性チェックを回避します。
解決策:一度にすべて構築する
interface Point { x: number; y: number; }
const pt: Point = {
x: 3,
y: 4,
};
// TypeScript verifies all required properties are present
スプレッドを使ってオブジェクトを結合する
Object.assign は使わないでください:
const pt = { x: 3, y: 4 };
const id = { name: 'Origin' };
const namedPoint = {};
Object.assign(namedPoint, pt, id);
namedPoint.name;
// ~~~~~ Property 'name' does not exist on type '{}'
代わりにスプレッド構文を使いましょう:
const pt = { x: 3, y: 4 };
const id = { name: 'Origin' };
const namedPoint = { ...pt, ...id };
// ^? { name: string; x: number; y: number }
namedPoint.name; // OK
オブジェクトを安全に組み立てる
各ステップで新しい変数を使います:
const pt0 = {};
const pt1 = { ...pt0, x: 3 };
const pt: Point = { ...pt1, y: 4 }; // OK
// Each variable has a new, complete type
条件付きプロパティ
スプレッドを使って条件付きでプロパティを追加します:
declare let hasMiddle: boolean;
const firstLast = { first: 'Harry', last: 'Truman' };
const president = {
...firstLast,
...(hasMiddle ? { middle: 'S' } : {}),
};
// ^? { middle?: string; first: string; last: string }
条件付きプロパティは、結果の型ではオプションになります。
複数の条件付きプロパティ
declare let hasDates: boolean;
const nameTitle = { name: 'Khufu', title: 'Pharaoh' };
const pharaoh = {
...nameTitle,
...(hasDates && { start: -2589, end: -2566 }),
};
// ^? { start?: number; end?: number; name: string; title: string }
start と end は条件付きでまとめて追加されるため、どちらもオプションになります。
オブジェクトの変換
データを変換するときは、関数型の構成要素を使います:
// Don't build incrementally
const result: Record<string, number> = {};
for (const item of items) {
result[item.name] = item.value; // Works but less type-safe
}
// Do use Array methods
const result = Object.fromEntries(
items.map(item => [item.name, item.value])
);
実例:設定オブジェクト
interface Config {
host: string;
port: number;
ssl?: boolean;
}
// Bad: incremental
const config = {} as Config;
config.host = 'localhost'; // Might forget port!
// Good: all at once
const config: Config = {
host: 'localhost',
port: 8080,
};
// Good: with conditional
const config: Config = {
host: 'localhost',
port: 8080,
...(useSSL && { ssl: true }),
};
プレッシャーへの対処プロトコル
1. 「動的に構築する必要があります」
プレッシャー:「プロパティは複数のソースから取得されます」
**回答:**まずすべてのデータを集めてから、オブジェクトを構築します。
**アクション:**スプレッドを使って結合します:{ ...source1, ...source2 }
2. 「型アサーションでうまくいきます」
プレッシャー:「as Type を使えばエラーを修正できます」
**回答:**型チェックを回避しているだけです。不足しているプロパティは検出されません。
**アクション:**オブジェクトを完成した状態で構築し、TypeScript に検証させましょう。
危険信号 — 立ち止まって再検討する
const obj = {}の後にプロパティを代入している- オブジェクトの構築に
Object.assignを使っている - 「プロパティが存在しない」エラーを抑えるために型アサーション(
as)を使っている - ループ内でオブジェクトにプロパティを追加している
よくある誤った正当化
| 言い訳 | 実情 |
|---|---|
| 「まだすべてのプロパティがわかりません」 | 先にデータを集めてから、オブジェクトを構築します |
| 「段階的なほうが読みやすいです」 | オブジェクトリテラルは明快で型安全です |
| 「型アサーションで修正できます」 | 型アサーションは型チェックを回避します |
クイックリファレンス
// DON'T: Build incrementally
const obj = {};
obj.a = 1; // Error
// DON'T: Use type assertion
const obj = {} as MyType;
// DO: Build all at once
const obj: MyType = { a: 1, b: 2 };
// DO: Combine with spread
const obj = { ...base, ...extra };
// DO: Conditional properties
const obj = { ...base, ...(cond && { opt: val }) };
要点
オブジェクトは1つの文ですべて完成させましょう。
TypeScript の型は作成時に固定されます。オブジェクトリテラルを使って、すべてのプロパティを一度に定義します。スプレッド構文を使ってオブジェクトを結合し、条件付きプロパティを追加します。型アサーションは避けましょう。
参考資料
Dan Vanderkam 著「Effective TypeScript」の項目 21:オブジェクトは一度に作成する。