typescript について
型、API、実装を設計する開発者向けの、プロジェクト全体で適用する TypeScript 規約です。既存の型定義の再利用、ファクトリー関数、型安全性、コードナビゲーションを重視します。
主な機能
- 01既存型の推論またはインポートを優先
- 02interface ではなく type を使用
- 03クラスよりファクトリー関数を優先
- 04型のエクスポートとコード移動を規定
- 05型安全性と網羅性チェックを重視
活用シーン
- 01TypeScript プロジェクトの規約を統一
- 02再利用可能な型とファクトリー API の設計
- 03エクスポートと Go-to-Definition の確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ epicenterhq/ epicenter --skill typescriptインストール後、Claude Code で/typescriptを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/advanced-typescript-features.mdfactory-patterns.mdproject-conventions.mdreadable-constraint-errors.mdruntime-schema-patterns.mdtesting-patterns.mdtype-organization.mdtype-safety-and-control-flow.md詳細
TypeScript ガイドライン
プロジェクト全体の TypeScript 規約は、arktype、typebox、testing、factory-function-composition などのより限定的なスキルと組み合わせて適用されます。
基本ルール
- 新しい名前付き型を宣言する前に、既存の型から導出するかインポートしてください。新しい名前付き型は、実際のコントラクト、プロトコル用語、判別可能な結果ユニオン、能力ポート、または複数の実装に対応する形状として必要性がある場合に限って導入してください。
- ローカルに形状を複製することは、境界に問題がある兆候と捉えてください。型を所有するランタイム型、スキーマ推論、ファクトリーの戻り値の型、関数シグネチャ、または呼び出し元が所有する能力関数を優先してください。
- 名前付きのベース型から上位へ合成してください。
Base & Extraを使って拡張し、Omit<Base, 'k'> & { k: U }のように、除外して置き換える方法は使わないでください。自分で定義した型にOmit<...> &がある場合、それは構造を上書きしていることを示しており、より小さなベース型に名前を付ける必要があるというサインです。サーフェスを絞り込むには、メンバーをOmitで除去せず、そのベース型を返してください。プロジェクト規約を参照してください。 interfaceではなくtypeを使ってください。readonlyは、上流の型と完全に一致させる場合を除き、配列とマップにのみ使ってください。- 頭字語は camelCase では通常の単語として扱ってください。例:
parseUrl、defineTable、readJson、customerId。 - 相対インポートには
.js拡張子を使ってください。拡張子なし、または.tsの相対インポートは使わないでください。 - シンボルは宣言箇所でエクスポートしてください。
export { ... } from ...はバレルファイルに限って使ってください。 - React/TSX では、
React.FC/FunctionComponentより、明示的な props パラメーターを持つ名前付き関数コンポーネントを優先してください。コンポーネントの値をレジストリーに格納する場合、またはデータとして渡す場合に限り、コンポーネントの値に型を付けてください。 - クラスよりファクトリー関数を優先してください。クロージャー内の位置によって、プライベート API とパブリック API の違いを表現してください。
TSchema、TDefs、TKeyのように、Tプレフィックスを付けた説明的なジェネリック名を使ってください。- オブジェクトが設定のまとまりである場合、関数シグネチャ内でオプションを分割代入してください。変換または転送するドメインオブジェクトである場合に限り、名前付きの値として保持してください。
- プライベートおよび内部の戻り値の型は TypeScript に推論させてください。エクスポートする API には、明確さの向上や循環推論の回避に役立つ場合に限り、型注釈を付けてください。
- エクスポートする型が
create*、attach*、open*などのファクトリーから返されるオブジェクトと完全に一致する場合は、ReturnType<typeof createThing>を使って実装から導出してください。エクスポートする出力の型エイリアスは、ファクトリーの直後に置いてください。入力、設定、データ、プロトコル、および複数の実装に対応するコントラクトの型は、ファクトリーより前に置いてください。 - 利用者向けの JSDoc は、返されるオブジェクトのメンバーに付けてください。IntelliSense を維持する場合、実装の詳細を絞り込む場合、またはパブリックなメソッド/プロパティのサーフェスを安定させる場合は、返されるオブジェクト内のメンバーに具体的な型注釈を付けてください。
- カリー化されたファクトリーでは、
ReturnType<ReturnType<typeof createThing>>のように、内側の戻り値から型を導出してください。ジェネリックファクトリーでは、必要に応じてReturnType<typeof openThing<TActions>>のようにtypeofをインスタンス化してください。 - オブジェクトリテラルから型を導出する場合、意図的に
readonlyにしているパブリックなサーフェスはゲッターで維持してください。具体的な実装で内部状態をたまたま書き込み可能な形で保持しているからといって、その状態を公開しないでください。 - 同一性が偶然一致した形状の検査ではなく、特定のファクトリー出力を意味する場合は、
Symbolブランドを使ってください。 as anyは避けてください。代わりにunknown、検証、ブランド、またはより限定的なヘルパーを使ってください。- オプショナルなプロパティを調べるときは、
inチェックや真偽値による判定より、オプショナルチェーンを優先してください。 - 質問に答える真偽値には、
is、has、またはcanのプレフィックスを使ってください。 - 同じ値に対する等値比較を繰り返す場合は、
if/elseよりswitchを優先してください。必要に応じて網羅性の確認にはdefault: value satisfies neverを使ってください。 - 有限個の値のマッピングには、入れ子の三項演算子より
Recordのルックアップテーブルを優先してください。 - 型付きエラーは下位から合成してください。境界で広範な上流エラーユニオンをフィルタリングしないでください。Result の伝播、回復、表示については
error-handlingを参照してください。 - 無効な状態を隠すサイレントフォールバックには疑問を持ってください。解析とシリアライズでは、往復の不変条件を維持してください。
Go-to-Definition の認識
型やエクスポートを整理するときは、常に Go-to-Definition を考慮してください。開発者が呼び出し元から Go-to-Def を実行したとき、実際のソースにできるだけ近い場所に移動できるべきです。設計上の選択によって余分な移動が発生する場合は、実際の検証境界、公開された契約、複数実装を持つポートなど、別の面でその選択に見合うメリットが必要です。
注意すべき具体的なリグレッション:
- モジュールレベルオブジェクトの分割代入による再エクスポート:
const stub = { fn, gn } satisfies T; export const { fn, gn } = stub;では、Go-to-Def の移動先が実際の定義ではなく、分割代入の行になります。エクスポートごとにsatisfiesを指定するか、export const fn = ... satisfies T['fn']のように直接エクスポートしてください。 satisfiesではなくtypeof Realによる注釈:export const fn: typeof Real = unreachableでは、ナビゲーションで元の値の識別情報が失われます。export const fn = unreachable satisfies typeof Realなら、その値をソースとして保てます。- 複数実装ポートでの
satisfiesではなく: Tによる注釈:これは上記のルールの補完です。インターフェースTに複数の実装(#platform/*や browser/tauri の分岐)があり、ある実装が意図的にTより狭い場合(たとえば、契約で宣言されているパラメーターを無視する場合)、export const x = {...} satisfies Tでは、その狭い具象型が漏れ出し、プラットフォームによって呼び出し元から見たメソッドの型が変わります。export const x: T = {...}と注釈して広い契約を公開してください。そうすれば、狭い実装でも型チェックを通過します。参考:whispering のManualRecorderLive: RecorderService<...>(バイナリー契約の背後にある単項 CPAL 実装)。 - バレル以外のファイルでの再エクスポートチェーン:
index.ts以外でのexport { X } from './alias'は、何も得られないまま余分な移動を発生させます。export { ... } from ...はバレルに限定し、それ以外の場所では宣言と同時にエクスポートしてください。 - 振る舞いを変えないアダプター/プロキシ/ラッパー:
fromXトランスレーターや薄いパススルーは、Go-to-Def の移動先をラッパーにしてしまいます。代わりに、基盤となるファクトリーの戻り値の形を広げてください(factory-function-compositionの「collapsed adapter」ルールを参照)。 - ゾーン 4 と重複する手書きの戻り値型注釈:ファクトリーに手書きのインターフェースを注釈すると、Go-to-Def の移動先がエイリアスになります。ファクトリーから具象オブジェクトを返し、その直後に
export type Thing = ReturnType<typeof createThing>としてエクスポートするエイリアスを置いてください。これにより、ナビゲーションの移動先を返されたメンバーに保ち、それらの JSDoc が公開ドキュメントを担うようにできます。ホバーやナビゲーションに関する疑問は、ファクトリーメソッドのドキュメントを参照してください。 - ノイズの多い
satisfiesのジェネリックリスト:戻り値のオブジェクトがジェネリック契約を満たすことを証明する必要がある一方で、satisfies Contract<A, B, C> & Extrasによって、呼び出し元が推論されたテーブル型、アクション型、ランタイム型を再度指定しなければならない場合は、契約モジュールが所有する制約付き ID ヘルパーを優先してください。例:ヘルパーがTWorkspace extends Workspace<...>を受け取り、TWorkspaceを返すようにして、return defineWorkspace({ ...workspace, ...runtime })とします。これにより、呼び出し元を読みやすく保ち、正確に推論された戻り値の型を維持し、Go-to-Def の移動先を実際のオブジェクトメンバーにできます。 defineXを追加しない場合:ヘルパー名を付けるためだけに、単純なsatisfiesチェックをラップしないでください。契約に必須の型引数がない場合や、ジェネリックにデフォルト値があってsatisfies Contractが読みやすい場合は、satisfiesを優先してください。ヘルパーを追加する価値があるのは、読み手がそれ以外では把握しなければならないジェネリックのノイズを取り除ける場合だけです。
パッケージ間のナビゲーションに影響する、より広範な公開シェイプの設計判断については、rethink を参照してください。
参照マップ
- プロジェクトの規約:派生型、ローカルな形状コピー、インポート、バレル、ファクトリー、ジェネリクス、分割代入、ファクトリーの戻り値の型に関する詳細な例です。
- 型安全性と制御フロー:識別用ブランド、型アサーション、オプショナルプロパティ、ブール値の命名、switch 文、レコードの検索、エラーの合成、フォールバックの問題、往復不変条件について説明します。
- 型の整理:
types.tsの配置、同じ場所にまとめるルール、インライン化と抽出のホップテスト、オプションと ID の命名について説明します。 - ファクトリーのパターン:ファクトリーに焦点を当てたリファクタリング、引数の分割代入、関連する状態の抽出について説明します。
- ランタイムスキーマのパターン:arktype、ブランド付き ID、オプショナルプロパティの構文、ワークスペースのテーブル ID について説明します。
- テストのパターン:インライン化した一度だけ使用するセットアップ、ソースをシャドーイングするテストについて説明します。
- 高度な TypeScript 機能:イテレーターヘルパーと const ジェネリクスによる配列の型推論について説明します。
- 読みやすい制約エラー:コンパイラ診断で問題の値を特定する必要がある場合に、リテラルキーや文字列の制約を確認します。
コメント
Markdown 対応