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 での強制アサートは最後の手段。
よくある原因
- 文字列リテラルが
stringに広がっている。
例:function setMode(m: 'light' | 'dark') {}にconst m = 'light'; setMode(m)と渡すと、mの推論はstringになり代入不可。 T | nullを返す API の戻り値をT専用の関数に流し込んでいる。
null チェックなしでは渡せない。- オブジェクトリテラルが、宣言されていない余剰プロパティを含む。
あるいは required なプロパティが抜けている。 - ジェネリック関数の呼び出しで型引数を省略しており、推論が
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 を参照。