microsoft / microsoft/TypeScript

Make calling "pseudo-overloads" behave more like calling true overloads

Abierto
#42,987 1 comentario 4 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

In Discussion Suggestion
Lenguaje dominante
Go
Estrellas
111k
Forks
14.3k
Merge medio
1 d 19 h
PR fusionados (30 d)
117

Descripción

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.


Playground link

Related issues:
#31977: better intellisense for discriminated union tuples
#38234: treat functions with unions of rest tuples as a rest argument as "pseudo-overloads"

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Comienza con los ejemplos motivadores que comparan las sobrecargas reales y los parámetros rest que contienen uniones de tuplas. Revisa las issues relacionadas #31977 y #38234 y determina después cómo debería la tipificación contextual resolver el parámetro del callback. Se considera terminado cuando las llamadas pseudo-sobrecargadas admiten la tipificación contextual deseada, similar a la de las sobrecargas, sin cambiar el comportamiento en tiempo de ejecución.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
typescript
Área
compilers
Tipo de issue
Nueva funcionalidad
Dificultad
5/5
Tiempo estimado
Más de una semana
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
30/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.