Skip to content

Allow binding generic functions to a given type #37181

Closed
@mperktold

Description

@mperktold

Search Terms

generic function, parametric function, function type parameter, function type argument

The problem is closely related to #26043, but the proposed solution is different.

Suggestion

Currently, there is no way of explicitly providing type arguments to generic functions. For example, consider the following function:

function box<T>(value: T) {
  return { value };
}

TypeScript correctly infers the return type { value: T }. However, it's not possible to derive the return type for a particular T, say number, at least not without ugly workarounds such as creating new functions just to fix the type arguments:

type Box = ReturnType<typeof box>;  // { value: unknown }
type Box<T> = ReturnType<typeof ???>;  // can't propagate T from Box to box

const numBox = (n: number) => box(n);  // ugly workaround
type NumBox = ReturnType<typeof numBox>;

So I suggest to provide a syntax that allows to do just that: fix type arguments of generic functions:

type Box<T> = ReturnType<typeof box<T>>;
type NumBox = ReturnType<typeof box<number>>;

This syntax resembles the existing one for providing type arguments when calling a function:

const b = box<number>(2);

With this proposal implemented, interpreting such a call could be split into two parts:
First fix the type argument of box to number, which yields a function that takes a number.
Second, apply that function to 2.
Hence, it could also be written with parenthesis: (box<number>)(2).
I wouldn't suggest doing that in practice, but it shows that this proposal increases composability of generic functions.

Use Cases

I tried to come up with a way to implement discriminated unions more succintly. The idea is to start with factory functions and then combine the return types:

function val<V>(value: V) {
    return { type: "Val", value } as const;
}
function plus<V>(left: V, right: V) {
    return { type: "Plus", left, right } as const;
}
function times<V>(left: V, right: V) {
    return { type: "Times", left, right } as const;
}

type Expr<V> = ReturnType<typeof val<V>>
             | ReturnType<typeof plus<V>>
             | ReturnType<typeof times<V>>;

With this approach, you would get convenient factory functions for building instances, without duplicating the object structure.

But it doesn't work, because it relies on the new syntax I just proposed.

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, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

Metadata

Metadata

Assignees

No one assigned

    Labels

    CommittedThe team has roadmapped this issueSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions