

困りごとから先に書くと、下記コードの `return fn(...args);` の部分でエラーが発生し、それがどうやっても取れません。原因は何でしょうか？

[code]

    const table = {
        add: (a: number, b: number) => a + b,
        square: (a: number) => a * a,
    } as const;
    
    type Table = typeof table;
    type Key = keyof Table;
    
    function calculate<T extends Key>(key: T, ...args: Parameters<Table[T]>): number {
        const fn: Table[T] = table[key];
        return fn(...args);
    }
    
    const x = calculate('add', 1, 2);
    const y = calculate('square', 3);
    
[/code]

上のコードでやろうとしていることは、まずテーブルに関数を並べ、キーを使ってそのうちの一つを呼び出す、いわゆるヘルパー関数を実装することです。末尾2行が使用例で、このようにキーに応じて型推論がうまく効き、呼び出し側に正しい引数の型を強いることを狙いとしています。そして末尾2行にエラーが出ていないことから、この点では型推論はうまく行っているようです（`add`
と `square` で引数の数が逆だとエラーが出ます）。

ところが、`calculate` 関数内の `return fn(...args);` の部分において、`fn` の型は `T`
が決まるまで決まらないはずですが、エディタを見るとここが `(a: number, b: number) => number`
型に決め打ちされているようで、それがエラーを呼び込んでいるように見えます。

問題の細分化のために、試しに `calculate`
の実装にジェネリックを使うのをやめて、下記のように特定のリテラル型限定で書くとエラーが消えました（そのため、自分にはコンパイラのバグに思えてなりません）。

[code]

    function calculate(key: 'add', ...args: Parameters<Table['add']>): number {
        const fn: Table['add'] = table[key];
        return fn(...args);
    }
    
[/code]

型推論に詳しい方、原因がお分かりでしたらご教示ください。よろしくお願いします。

補足：

  * 今回についてはエラーメッセージは本質でないと思いますが、一応書くと `spread 引数には、組の種類を指定するか、rest パラメーターに渡す必要があります。ts(2556)` でした
  * 回りくどいコードに見えるかもしれませんが、これはエラーをあぶり出すために最小化したコードであり、このような形にしなければならない理由は省略しています


CC BY-SA 4.0
2022-03-07T08:39:02.720
86732
2022-03-08T04:19:18.727
51728
question
typescript
ルックアップ型と可変長引数の組み合わせで型推論がうまく行かない


自己解決しました。型制限 `<T extends Key>` は決して `Key` のうちの **一つであること** を要求しないんですね。

上の例で `T` は `'add'` か `'square'` のどちらかに決まると思っていましたが、論理的には `'add' | 'square'`
もあり得るわけで、そう考えると `fn` の型もこの時点で確定することはできませんね。

ユニオン型のうちの一つであることを要求する記述 `oneof` の提案について、この
[issue](https://github.com/microsoft/TypeScript/issues/27808) で議論されているようです。


CC BY-SA 4.0
2022-03-08T04:19:18.727
86746
2022-03-08T04:19:18.727
51740
86732
answer