Skip to content

Spreading tuple into generic/type arguments #39648

Open

Description

Search Terms

  • spreading generic parameters
  • generic parameter spread
  • tuple generic spread
  • spreading generic argument
  • spreading typeArgument
  • spreading type argument

Suggestion

Allow spreading of a tuple type into type arguments.

The idea is that a tuple of n elements gets spread into generic arguments with r arguments. If n is larger than r, only the first r items are used. If n is smaller than r it is possible to add elements after the spread argument. E.g.

function foo<A, B, C, D>() {
}

type ThreeType = [Foo, Bar, Baz];

foo<...ThreeType, Delta>();

Use Cases

I find myself repeating complex generic types. Ideally I could shorten lengthy repetition into a single type.

Examples

dialog.open takes three generic arguments, these have to be repeated each call. This makes the call less legible and reduces DRYness.

const dialogRef = this.dialog.open<
    PrinterSelectDialogComponent,
    PrinterSelectDialogData,
    OfferProductPrinter>(
        PrinterSelectDialogComponent,
        {
            data: {
                printer,
            },
        },
    );

After this proposal

export type PrinterSelectDialog = [
    PrinterSelectDialogComponent,
    PrinterSelectDialogData,
    OfferProductPrinter,
];


const dialogRef = this.dialog.open<...PrinterSelectDialog>(PrinterSelectDialogComponent, {
    data: {
        service,
    },
});

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.

Activity

  1. somebody1234 commented on Aug 9, 2021

    @somebody1234

    one workaround could potentially be using an object (?) type instead?

    function foo<T extends { Foo: unknown; Bar: unknown; Baz: unknown; Quux: unknown; }>(t: T['Foo'], u: T['Baz']): [T['Foo'], T['Baz']] {
        return [t, u];
    }
    
    type Foo = 1;
    type Bar = 2;
    type Baz = 3;
    type Quux = 4;
    
    type ThreeType = { Foo: Foo; Bar: Bar; Baz: Baz; };
    
     foo<ThreeType & { Quux: Quux; }>(1, 3);
    // ^?
  2. byronbroughten commented on Jan 20, 2022

    @byronbroughten

    This would be particularly useful for interacting with libraries that use generics that take multiple arguments rather than an object type. A situation I find myself in.

  3. scottwio commented on Oct 14, 2022

    @scottwio

    Find myself in the same issue, third party so can't move to object

  4. wesbos commented on Jan 25, 2023

    @wesbos

    Running into this with Express - though I can say it might just be their Typings because I've never needed this before.

    I want to extend their Request and have to use something like this:

    export interface AuthedRequest<
        P = ParamsDictionary,
        ResBody = any,
        ReqBody = any,
        ReqQuery = ParsedQs,
        LocalsObj extends Record<string, any> = Record<string, any>
    > extends Request<P, ResBody, ReqBody, ReqQuery, LocalsObj> {
      test: string
    }
  5. callumacrae commented on Jun 14, 2023

    @callumacrae

    same issue here!

    current code:

    type UpdateLabelData = Label;
    type UpdateLabelError = unknown;
    type UpdateLabelVariables = LabelArguments;
    type UpdateLabelContext = { previousLabels: Label[] };
    export function useUpdateLabel(
      options?: UseMutationOptions<
        UpdateLabelData,
        UpdateLabelError,
        UpdateLabelVariables,
        UpdateLabelContext
      >,
    ) {
      const mutation = useMutation<
        UpdateLabelData,
        UpdateLabelError,
        UpdateLabelVariables,
        UpdateLabelContext
      >({

    would love:

    type UpdateLabelGeneric = [Label, unknown, LabelArguments, { previousLabels: Label[] }];
    export function useUpdateLabel(
      options?: UseMutationOptions<...UpdateLabelGeneric>,
    ) {
      const mutation = useMutation<...UpdateLabelGeneric>({
  6. roryabraham commented on Jan 30, 2024

    @roryabraham

    This would be cool – I naïvely requested this feature in type-fest as syntactic sugar to allow the Merge function to merge N types without nesting:

    // before
    type MyType = Merge<
        FinalDestination,
        Merge<
            IntermediateDestination,
            Source,
        >,
    >;
    
    // After
    type MyType = Merge<FinalDestination, IntermediateDestination, Source> 
  7. somebody1234 commented on Jan 30, 2024

    @somebody1234

    Rory Abraham (@roryabraham) nope - this kind is 100% possible. just:

    • make it accept (say) 8 parameters
    • add defaults to all but the first two. what the defaults are will depend on the type. this can be unknown, never, or a custom unique symbol used as a sentinel value.

    and that's it

    (what this proposal would be adding is the ability to bundle up the array of parameters and pass the array around)

  8. immjs commented on Aug 23, 2026

    @immjs

    I believe this would also be useful to do some meta-programming, where in my case, I am taking a class constructor and extending it within a function. This would allow me to extract all the type parameters from the input constructor, and apply them to the output constructor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions