Make calling "pseudo-overloads" behave more like calling true overloads
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 30/100
- issue の種類
- 機能追加
- 明瞭さ
- おおむね明確
- 活発さ
- 停滞
- 技術スタック
- typescript
- 領域
- compilers
調査の方向性
真のオーバーロードとタプルのユニオンを含む rest パラメーターを比較する動機付けとなる例から始めます。関連する issue #31977 と #38234 を確認し、そのうえで contextual typing によって callback パラメーターをどのように解決すべきかを判断します。pseudo-overloaded な呼び出しが、ランタイムの動作を変更せずに、望ましいオーバーロードに似た contextual typing をサポートできれば完了です。
索引モデルが issue の本文から書いたものです。
説明
Suggestion
🔍 Search Terms
rest tuple, union, overloads, contextual typing
✅ Viability Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
⭐ Suggestion
Bring the behavior when calling "pseudo-overloaded" functions (functions whose rest parameter is of a union of tuple types) in line with the behavior when calling "true overloaded" functions.
📃 Motivating Example
When you call a true overloaded function, the compiler will, for example, contextually type a callback parameter based on the resolved call signature:
function overloads(v: string, f: (v: string) => void): void;
function overloads(v: number, f: (v: number) => void): void;
function overloads() { }
overloads(123, (v: number) => v.toFixed());
overloads("abc", (v: string) => v.toUpperCase());
overloads(123, v => v.toFixed()); // okay
Whereas the "pseudo-overloaded" version of this (which IntelliSense presents as overloads as of #38234), the compiler is not able to do such contextual typing:
function restTuple(...args:
[v: string, f: (v: string) => void] |
[v: number, f: (v: number) => void]
) { }
// 1/2 restTuple(v: string, f: (v: string) => void): void
// 2/2 restTuple(v: numbger, f: (v: number) => void): void
restTuple(123, (v: number) => v.toFixed());
restTuple("abc", (v: string) => v.toUpperCase());
restTuple(123, v => v.toFixed()); // error!
// ----------> ~
// Parameter 'v' implicitly has an 'any' type.
It would be nice (although I understand that it might not be easily done) if callers could treat pseudo-overloads as true overloads, or at least more like true overloads.
💻 Use Cases
None of these are show-stoppers, btw
allow refactoring of overloads/generics to pseudo-overloads without affecting callers
In situations where the return type of a function does not depend on the parameter types, you can get some better behavior inside a function implementation with a pseudo-overload instead of either a generic that extends a union (see #13995) or a true overload (#18533):
function gen<K extends "s" | "n">(type: K, val: { s: string, n: number }[K]): string {
return (type === "s") ? val.toUpperCase() : val.toFixed(); // error! can't narrow K this way
}
function ovl(type: "s", val: string): string;
function ovl(type: "n", val: number): string;
function ovl(type: "s" | "n", val: string | number) {
return (type === "s") ? val.toUpperCase() : val.toFixed(); // error! type and val are uncorrelated
}
function pseudo(...args: [type: "s", val: string] | [type: "n", val: number]): string {
const [type, val] = args;
(type === "s") ? val.toUpperCase() : val.toFixed(); // error, type and val are still uncorrelated, #30581
return (args[0] === "s") ? args[1].toUpperCase() : args[1].toFixed(); // but this works!
}
I'm not thrilled about not being able to destructure args before the check of args[0], but at least it's possible to get some discriminated union type checking in the implementation. From the caller's side, though, it would be nice if I didn't have to worry about the difference between ovl()'s and pseudo()'s call signatures. And since IntelliSense presents them as overloads in some situations, it can lead to confusion when two "same" functions behave differently at call sites.
programmatic generation of overloaded function call signatures:
If pseudo-overloads behaved more like overloads from the caller side, I'd have no reservations suggesting something like this:
type Params = [type: "s", val: string] | [type: "n", val: number] | [type: "b", val: boolean]
declare const p: (...args: Params) => string;
p("s", "");
p("n", 1);
p("b", true);
Otherwise, I need to do something union-to-intersection-like the following:
declare const o:
(Params extends infer P ? P extends Params ?
(x: (...args: P) => string) => void : never : never
) extends ((x: infer F) => void) ? F : never;
o("s", "");
o("n", 1);
o("b", true);
which is fun but ugly.
Related issues:
#31977: better intellisense for discriminated union tuples
#38234: treat functions with unions of rest tuples as a rest argument as "pseudo-overloads"
- 主要言語
- Go
- スター
- 111k
- フォーク
- 14.4k
- 平均マージ
- 1日 15時間
- マージ済み PR(30日)
- 106
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
microsoft/TypeScript のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
microsoft/TypeScript#64322 · コメント 2 件 · リアクション 1 件 · 担当者 2 名 ·
-
Possible Improvement
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
microsoft/TypeScript#64278 · コメント 1 件 · リアクション 1 件 ·
-
Docs
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
microsoft/TypeScript#64118 · コメント 1 件 ·
-
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
microsoft/TypeScript#64094 ·
-
Docs
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
microsoft/TypeScript#63959 · コメント 5 件 ·
microsoft/TypeScript の issue をすべて見る
似ている issue
-
Type/Bug
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
OpenNSW/nsw-srilanka#497 ·
-
難易度 1/5 1時間未満 初心者へのやさしさ 92/100
milvus-io/birdwatcher#545 ·
-
kind/bug
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
kubernetes-sigs/prow#953 · コメント 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
-
enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
JuliusBrussee/caveman#1102 · コメント 1 件 ·