mcpField
P
precise-string-types

precise-string-types

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

precise-string-types について

文字列の有効値が限られる場合に、より厳密な型を使うよう促す TypeScript 向けコーディングスキルです。リテラルユニオン、keyof、テンプレートリテラル型を扱い、安全で明確な API 設計に役立ちます。

主な機能

  1. 01文字列リテラルユニオンの指針
  2. 02オブジェクトキーに keyof を使用
  3. 03パターンにテンプレートリテラル型
  4. 04型安全な API 引数の提案
  5. 05文字列型の移行手順

活用シーン

  1. 01引数を有効値に限定
  2. 02関数引数をオブジェクトキーに限定
  3. 03定義済みパターンの文字列に型を付与

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill precise-string-types

インストール後、Claude Code で/precise-string-typesを入力して読み込まれたか確認します。

詳細

string 型よりも、より正確な代替型を優先する

概要

string 型は範囲が広大です。可能な限り、より狭い型を使用してください。

文字列リテラル型のユニオン、テンプレートリテラル型、keyof を使うと、単なる string よりも優れた型安全性、オートコンプリート、ドキュメントを得られます。

このスキルを使う場面

  • 有効な値が限られたプロパティを定義するとき
  • 特定の文字列を受け取る関数パラメーターを作成するとき
  • オブジェクトのキーやプロパティ名を扱うとき
  • 「文字列型に依存しすぎた」コードを見かけたとき
  • 文字列パラメーターでオートコンプリートが表示されないとき

絶対のルール

NEVER use plain string when the valid values are limited.

例外はありません:

  • 「あとで値が増えるかもしれない」場合でも
  • 「そのほうが簡単だから」という場合でも
  • 「enum は大げさすぎる」という場合でも

検出:「文字列型に依存しすぎた」コードの兆候

文字列が特定の値しか取り得ないなら、それを明示してください。

// ❌ VIOLATION: Too broad - accepts any string
interface Album {
  artist: string;
  title: string;
  releaseDate: string;     // What format? Any string works!
  recordingType: string;   // "live" or "studio"... but also "banana"
}

// These are all valid but wrong:
const album: Album = {
  artist: 'Miles Davis',
  title: 'Kind of Blue',
  releaseDate: 'August 17th, 1959',  // Wrong format
  recordingType: 'Studio',            // Wrong case
};

解決策:文字列リテラル型のユニオン

// ✅ CORRECT: Precise types
type RecordingType = 'studio' | 'live';

interface Album {
  artist: string;
  title: string;
  releaseDate: Date;         // Use Date, not string
  recordingType: RecordingType;
}

const album: Album = {
  artist: 'Miles Davis',
  title: 'Kind of Blue',
  releaseDate: new Date('1959-08-17'),
  recordingType: 'Studio',
  //             ~~~~~~~~ Type '"Studio"' is not assignable to type 'RecordingType'
};

正確な型のメリット

1. タイプミスを検出できる
type Direction = 'north' | 'south' | 'east' | 'west';

function move(direction: Direction) { /* ... */ }

move('nroth');  // Error: Did you mean 'north'?
2. オートコンプリートが表示される
// With string: no suggestions
function move(direction: string) { }
move('|')  // Nothing

// With union: suggestions appear
function move(direction: Direction) { }
move('|')  // Suggests: north, south, east, west
3. 有効な値を明示できる
/** What environment was this recording made in? */
type RecordingType = 'live' | 'studio';

// The type IS the documentation
function getAlbums(type: RecordingType) { }
// IDE shows: (parameter) type: "live" | "studio"

オブジェクトのキーに keyof を使う

// ❌ BAD: Any string is accepted
function pluck(records: any[], key: string): any[] {
  return records.map(r => r[key]);
}

// ✅ GOOD: Only valid keys accepted
function pluck<T>(records: T[], key: keyof T): T[keyof T][] {
  return records.map(r => r[key]);
}

const albums: Album[] = [/* ... */];

pluck(albums, 'artist');     // OK, returns string[]
pluck(albums, 'artits');     // Error: Did you mean 'artist'?

さらに優れた方法:ジェネリックなキーのパラメーター

function pluck<T, K extends keyof T>(records: T[], key: K): T[K][] {
  return records.map(r => r[key]);
}

const dates = pluck(albums, 'releaseDate');
//    ^? const dates: Date[]  // Precise return type!

const artists = pluck(albums, 'artist');
//    ^? const artists: string[]

パターンにはテンプレートリテラル型を使う

// Match strings like "GET /users" or "POST /items"
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type ApiRoute = `${HttpMethod} /${string}`;

function handleRoute(route: ApiRoute) { }

handleRoute('GET /users');     // OK
handleRoute('POST /items');    // OK
handleRoute('PATCH /users');   // Error: not a valid HttpMethod
handleRoute('GET users');      // Error: missing /

string が適切な場合

interface Album {
  // These genuinely can be any string:
  artist: string;   // "Miles Davis", "Queen", etc.
  title: string;    // Unlimited possibilities
  
  // These should NOT be string:
  recordingType: RecordingType;  // Limited options
  genre: Genre;                   // Limited options
}

圧力に負けないためのプロトコル

1. 「値が増えるかもしれない」

圧力:「あとで録画タイプを追加したらどうするのですか?」

回答: ユニオンに追加してください。TypeScript が、対応する必要のある箇所をすべて知らせてくれます。

対応: まず既知の値から始めてください。必要になったら拡張します。

2. 「ユーザーは何でも入力できる」

圧力:「ユーザー入力にはどんな文字列も含まれる可能性があります」

回答: 境界で検証してください。内部では正確な型を使います。

対応: ユーザー入力を解析・検証してから、コード内では正確な型を使ってください。

3. 「enum は大げさすぎる」

圧力:「これに enum を作りたくありません」

回答: 文字列リテラル型のユニオンは enum よりも軽量で、同じように正確です。

対応: enum の代わりに type X = 'a' | 'b' | 'c' を使ってください。

危険信号 — 作業を止めて再検討する

  • 有効な文字列値を説明するコメントがある
  • 文字列パラメーターに対する実行時検証がある
  • 文字列を扱う switch 文に「ここには到達しないはず」という default 分岐がある
  • オートコンプリートが表示されない文字列パラメーターがある
  • 文字列値のタイプミスによるバグがある

よくある正当化(すべて誤り)

言い訳実際のところ
「ただの文字列です」限られた集合から選ばれる文字列です。そのことを表現してください。
「あとで検証します」境界で検証し、内部では型を使ってください。
「値が多すぎます」有限なら列挙してください。TypeScript が対応します。
「enum は見た目が悪い」代わりに文字列リテラル型のユニオンを使ってください。

クイックリファレンス

現在の型代わりに使うもの
有効な値が N 個ある string'value1' | 'value2' | ...
オブジェクトのキーを受け取るパラメーターkeyof T
パターンに従う文字列テンプレートリテラル型
文字列としての日時Date
文字列としての URLブランド型を検討する

移行パターン

// Step 1: Find strings with limited values
interface Config {
  environment: string;  // "dev", "staging", "prod"
}

// Step 2: Create the union type
type Environment = 'development' | 'staging' | 'production';

// Step 3: Use it
interface Config {
  environment: Environment;
}

// Step 4: Fix all the errors (that's the point!)
const config: Config = { environment: 'dev' };
//                                    ~~~~~ Did you mean 'development'?

まとめ

string は文字列型における any です。

値が限られているなら、その制約を表現してください。有限の集合には文字列リテラル型のユニオン、オブジェクトのキーには keyof、パターンにはテンプレートリテラル型を使いましょう。コードの安全性が高まり、IDE がより役立つようになります。

参考文献

Dan Vanderkam 著『Effective TypeScript』項目 35「Prefer More Precise Alternatives to String Types」に基づいています。

コメント