Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spreading tuple into generic/type arguments #39648

Open
5 tasks done
MatthiasKunnen opened this issue Jul 18, 2020 · 7 comments
Open
5 tasks done

Spreading tuple into generic/type arguments #39648

MatthiasKunnen opened this issue Jul 18, 2020 · 7 comments
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@MatthiasKunnen
Copy link

MatthiasKunnen commented Jul 18, 2020

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.
@RyanCavanaugh RyanCavanaugh added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Jul 20, 2020
@somebody1234
Copy link

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);
// ^?

@ByronBroughten
Copy link

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.

@scottwio
Copy link

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

@wesbos
Copy link

wesbos commented Jan 25, 2023

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
}

@callumacrae
Copy link

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>({

@roryabraham
Copy link

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> 

@somebody1234
Copy link

somebody1234 commented Jan 30, 2024

@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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

8 participants