mcpField
F
function-type-expressions

function-type-expressions

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

function-type-expressions について

関数式全体に型を割り当て、注釈の重複を減らし、戻り値を検査するTypeScriptスキルです。コールバックやラッパー、同じシグネチャを持つ関数を書く開発者向けです。

主な機能

  1. 01関数式全体への型指定
  2. 02同じシグネチャ間での型の再利用
  3. 03関数型からの引数型推論
  4. 04型に基づく戻り値の検査
  5. 05typeofによる既存シグネチャの照合

活用シーン

  1. 01ライブラリのコールバックへの型付け
  2. 02共通シグネチャを持つ関数の実装
  3. 03引数シグネチャを保った関数のラップ
  4. 04関連関数間での注釈の重複削減

インストール

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

$npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill function-type-expressions

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

詳細

可能な場合は関数式全体に型を適用する

概要

個々のパラメーターではなく、関数全体に一度に型を付けます。

関数式(関数宣言ではありません)を使用する場合、関数全体に型を適用できます。これにより、繰り返しが減り、型安全性が向上し、コードが読みやすくなります。

このスキルを使う場面

  • 同じシグネチャを持つ複数の関数を書く場合
  • ライブラリのコールバックを実装する場合
  • 既存の関数のシグネチャに合わせる場合
  • 既存の関数をラップまたは拡張する場合

鉄則

When functions share a signature, define the type ONCE and apply it to EACH function.

覚えておきましょう:

  • 関数式には型を適用できます
  • 関数型からパラメーターの型が推論されます
  • 戻り値の型は関数型と照合されます
  • 既存の関数のシグネチャに合わせるには typeof fn を使います

検出:シグネチャの繰り返し

// Repetitive - same signature 4 times
function add(a: number, b: number) { return a + b; }
function sub(a: number, b: number) { return a - b; }
function mul(a: number, b: number) { return a * b; }
function div(a: number, b: number) { return a / b; }

解決策:関数型

一度定義して、何度も使う
type BinaryFn = (a: number, b: number) => number;

const add: BinaryFn = (a, b) => a + b;  // Types inferred
const sub: BinaryFn = (a, b) => a - b;
const mul: BinaryFn = (a, b) => a * b;
const div: BinaryFn = (a, b) => a / b;

利点:

  • 型注釈を繰り返す必要がありません
  • 戻り値の型が自動的にチェックされます
  • 型注釈によるノイズがなくなり、ロジックがより見やすくなります
typeof で既存の関数に合わせる
// Match fetch's signature exactly
const checkedFetch: typeof fetch = async (input, init) => {
  const response = await fetch(input, init);
  if (!response.ok) {
    throw new Error(`Request failed: ${response.status}`);
  }
  return response;
};

TypeScript は次の型を推論します:

  • input: RequestInfo | URL
  • init?: RequestInit
  • 戻り値:Promise<Response>
パラメーターとともに戻り値の型を変更する
// Match parameters but change return type
async function fetchNumber(
  ...args: Parameters<typeof fetch>
): Promise<number> {
  const response = await checkedFetch(...args);
  return Number(await response.text());
}

関数宣言と関数式

// Statement - must annotate each parameter
function rollDice1(sides: number): number { /* ... */ }

// Expression - can apply type to entire function
type DiceRollFn = (sides: number) => number;
const rollDice2: DiceRollFn = (sides) => { /* ... */ };

よく使われる関数型のパターン

コールバック型
type EventHandler = (event: Event) => void;
type AsyncCallback<T> = () => Promise<T>;
type Comparator<T> = (a: T, b: T) => number;

const handleClick: EventHandler = (e) => {
  console.log(e.target);  // e is typed as Event
};
ジェネリック関数型
type Mapper<T, U> = (item: T, index: number) => U;

const double: Mapper<number, number> = (n) => n * 2;
const stringify: Mapper<number, string> = (n) => String(n);
インターフェイス構文(別の方法)
// Function type as interface
interface StringTransform {
  (input: string): string;
}

const toUpper: StringTransform = s => s.toUpperCase();

戻り値の型安全性

関数型を使うと、戻り値の型エラーを検出できます:

const checkedFetch: typeof fetch = async (input, init) => {
  const response = await fetch(input, init);
  if (!response.ok) {
    return new Error('Failed');  // Error!
    // Type 'Error' is not assignable to type 'Response'
  }
  return response;
};

関数型を使わない場合、呼び出し箇所で初めてエラーになります。

ライブラリのコールバック型

ライブラリでは、コールバック型が提供されていることがよくあります:

// React provides these
import { MouseEventHandler, ChangeEventHandler } from 'react';

const handleClick: MouseEventHandler<HTMLButtonElement> = (e) => {
  console.log(e.currentTarget.disabled);  // Fully typed
};

const handleChange: ChangeEventHandler<HTMLInputElement> = (e) => {
  console.log(e.target.value);  // Fully typed
};

関数型を使わない場面

単一の関数に過剰な設計を加えるのは避けましょう:

// Overkill for a single standalone function
type GreetFn = (name: string) => string;
const greet: GreetFn = (name) => `Hello, ${name}`;

// Just use a normal function statement
function greet(name: string): string {
  return `Hello, ${name}`;
}

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

1. 「関数宣言のほうが明確です」

圧力:「パラメーターと並べて型を見るほうが好みです」

**回答:**シグネチャを共有する場合、型を一元化するとノイズが減ります。

**対応:**2つ以上の関数が同じシグネチャを共有する場合は、関数型を使います。

2. 「ライブラリの型がわかりません」

圧力:「ライブラリ内のコールバック型を見つけられません」

回答:typeof existingFunction または Parameters<typeof fn> を使います。

**対応:**typeof で既存のシグネチャに合わせます。

危険信号 — 立ち止まって再検討する

  • 複数の関数で同じパラメーター型が繰り返されている
  • ラップ対象の関数とシグネチャを一致させるべきラッパー関数がある
  • 適切に型付けされていないコールバックがある

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

言い訳実際のところ
「パラメーターに型を付けたほうが明確です」4回繰り返す場合はそうではありません
「関数宣言のほうがシンプルです」型付きの関数式も同じくらい明確です
「any を使えばいいだけです」型安全性の利点がすべて失われます

クイックリファレンス

// Define function type
type Transform<T> = (value: T) => T;

// Apply to function expression
const double: Transform<number> = v => v * 2;

// Match existing function
const myFetch: typeof fetch = async (input, init) => { ... };

// Match parameters, change return
function wrapper(...args: Parameters<typeof original>): NewReturn { ... }

要点

シグネチャを共有する場合は、関数式全体に型を適用します。

これにより、繰り返しが減り、型定義が一元化され、戻り値の型エラーを発生箇所で検出できます。既存の関数シグネチャに正確に合わせるには、typeof を使います。

参考資料

Dan Vanderkam 著『Effective TypeScript』の項目 12「可能な場合は関数式全体に型を適用する」に基づいています。

コメント