mcpField
C
create-objects-all-at-once

create-objects-all-at-once

github.com/marius-townhouse開発643 回インストール38 週間前に更新
リポジトリ

create-objects-all-at-once について

オブジェクトのプロパティを後から追加せず、一度に完成させるTypeScript向けスキルです。オブジェクトリテラルとスプレッド構文を使い、型アサーションに頼らず型チェックを保つ方法を説明します。

主な機能

  1. 01全プロパティを一度に持つオブジェクトの作成
  2. 02スプレッド構文によるオブジェクト結合
  3. 03条件付きプロパティの安全な追加
  4. 04関数型の構文によるデータ変換
  5. 05安全でない型アサーションの回避

活用シーン

  1. 01型付き設定オブジェクトの構築
  2. 02複数ソースのプロパティ結合
  3. 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:オブジェクトは一度に作成する。

コメント