TypeScriptでオーバーロード実装された関数を使う関数を書いていたら 「この呼び出しに一致するオーバーロードはありません。」TS(2769) と言われた

英語だと 'No overload matches this call' って言われると思う。タイトルが長い。

TS書いてて、関数の引数に Options なオブジェクトを受け取って、そのオプションのプロパティによって返り値が違うという関数を書いていた(hoge関数とする)。

で、hoge関数を使いつつ、hoge関数が受け取れる引数の型を外部から渡せる関数(foo関数とする)を書いていた。←ここでタイトルのエラーで怒られてしまった。

コード

type A = {
    passNumber: number
    toReturnString?: undefined
}

type B = {
    passNumber: number
    toReturnString: true
}

type Options = A | B

function hoge(options: A): number
function hoge(options: B): string
// function hoge(options: Options): string | number
function hoge(options: Options) {
    if(options.toReturnString === true) {
        return options.passNumber.toString()
    } else {
        return options.passNumber
    }
}

// 普通に使う分には通る
const willNumber = hoge({passNumber:1})
const willString = hoge({passNumber:1,toReturnString:true})

console.log('willNumber',willNumber)
console.log('willString', willString)

// 通らない!!!!!!!!!!
function foo(options:Options) {
    return hoge(options)
}

Playground Link

コードの解説

上記コードブロックのhoge関数は toReturnString がoptionalで、trueだったら返り値がstringになるし、そうじゃなかったらnumberで返るという関数。

hoge関数をだけで使う分には通るのだけど、foo関数でhoge関数にargmentsで受け取ったオプションの同じ型を渡すとエラーになる(L33)。

答え

上記TS playgroundのL15のコメントアウト箇所を外せば通るようになる。

具体的にはhoge関数のオーバーロード記述の3つ目の行

function hoge(options: Options): string | number

感想

オーバーロードのimplements(実装記述)で書いていれば通るものだと思っていたのでハマってしまった。

オーバーロードの対応する型パターンは全部記述しないといけないらしいという学びがあった。

最初、hoge関数単体で使えばコンパイル通るしちゃんと返り値が意図通りのオーバーロードの挙動をしてくれていたのでオーバーロード実装側に問題があると気づけなかった。foo関数の実装側でなにか凡ミスがあるのかとウンウン唸って時間を使ってしまった。