できない.dev

TypeScript で「Argument of type ... is not assignable」が解消できない

TS2345「Argument of type X is not assignable to parameter of type Y」は、関数引数の型 X が期待する型 Y のサブタイプでないために出るエラー。
リテラル型は as const で保つ、null/undefined は型ガードで除外、オブジェクトは satisfies で検査、ジェネリックは型引数を明示、で大半は解決する。

#type-error#argument#assignability#ts2345

公開:

要約

エラー TS2345「Argument of type X is not assignable to parameter of type Y」は、関数を呼ぶときの実引数 X が宣言された仮引数の型 Y に代入できない(サブタイプではない)ことを示す。
原因はリテラル型の広がり、null/undefined の混入、余剰プロパティ、ジェネリック推論の失敗が大半。as const / satisfies / 型ガード / 明示的な型引数のいずれかで意図した型に絞れば消える。as での強制アサートは最後の手段。

よくある原因

  1. 文字列リテラルが string に広がっている。
    例: function setMode(m: 'light' | 'dark') {}const m = 'light'; setMode(m) と渡すと、m の推論は string になり代入不可。
  2. T | null を返す API の戻り値を T 専用の関数に流し込んでいる。
    null チェックなしでは渡せない。
  3. オブジェクトリテラルが、宣言されていない余剰プロパティを含む。
    あるいは required なプロパティが抜けている。
  4. ジェネリック関数の呼び出しで型引数を省略しており、推論が unknown や空オブジェクト {} で確定して、想定外の型として fail する。

解決策

1. リテラル型を保つ

function setMode(m: "light" | "dark") {}
const m = "light" as const;       // 型は "light"
setMode(m);                       // OK
 
// あるいは関数宣言側で型注釈を付ける
const m2: "light" | "dark" = "light";

2. null / undefined を除外する

function need(s: string) {}
const maybe: string | null = getValue();
if (maybe != null) need(maybe);          // 型ガード
need(maybe ?? "");                       // デフォルト値

3. satisfies で形を保ちつつ検査する

type Config = { host: string; port: number };
const cfg = {
  host: "localhost",
  port: 5432,
} satisfies Config;

satisfies は型が一致しているか検査するが、推論されるリテラル情報はそのまま残るので、後段の cfg.host"localhost" リテラル型として使える(公式リリースノート)。

4. ジェネリックは型引数を明示する

function pick<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; }
pick<{ a: number }, "a">({ a: 1 }, "a");   // 推論失敗時はこのように明示

as は最後の手段

x as Y は型チェックを黙らせるだけで、ランタイムの不整合は残る。
先に上の 4 手を試して、本当に型情報の不足が確実な場合のみ使う。
基本的な型システムの考え方は 公式 Handbook を参照。

この記事は役立ちましたか?