Typescript: Rest elements in generic tuple

Created on 1 Aug 2018  Β·  6Comments  Β·  Source: microsoft/TypeScript


TypeScript Version: 3.1.0-dev.20180801


Search Terms: spread tuple

Code

interface Foo<ArgsT extends any[]> {}

interface Bar<SubArgsT extends any[]> extends Foo<[number, ...SubArgsT]>{ }

Expected behavior: Successful compilation. Array subclasses are not allowed as rest arguments for now, but when it is about generics I expect behavior similar to what is done in https://github.com/Microsoft/TypeScript/pull/24897

Actual behavior: Failed with _TS2574: A rest element type must be an array type._

Fix Available In Discussion Suggestion

Most helpful comment

A minimal example showing the non-symmetricity of function arguments vs. tuple types.

declare function foo<T0, T extends any[]>(a: T0, ...b: T); // works
type foo<T0, T extends any[]> = [T0, ...T]; // rest element type must be an array type

I think this should be supported and it seems as an oversight to me that it isn't in 3.1 already.
Is there a specific reason for it to be disallowed, or is it just that it wasn't top priority?


Also, it took me quite a long time to find this issue, so I'm adding a few keywords.
Keywords: spread a generic tuple in a tuple type, generic tuple rest parameter in a tuple type, spreading array type in type declaration, T extends any[] does not work in tuple literal

All 6 comments

Interstingly, adding angled brackets works (but requires an array of arrays following the number):

interface Foo<ArgsT extends any[]> {
    arr: ArgsT;
}

interface Bar<SubArgsT extends any[]> extends Foo<[number, ...SubArgsT[]]>{ }

var f: Foo<number[]> = {
    arr: [1,2,3],
};

var b1: Bar<string[]> = {
    arr: [1, [""], ["1", "2"]], // not what OP meant, but it works
}
var b2: Bar<string[]> = {
    arr: [1, "", "1", "2"], // error: [number, string, string] cannot be assigned to [number, ...string[][]]
}

And this correctly captures the type OP wants to create

type Fooify<SubArgsT extends any[]> = 
    ((arg1: number, ...rest: SubArgsT) => any) extends ((...args: infer R) => any)
    ? Foo<R> : never;

but an interface cannot extend that.

Similarly, it seems that following the last example of @AlCalzone that rest element types should be able to be inferred without having to use a functions parameter list like so:

type dropFirst<T extends any[]> = 
    T extends [any, ...(infer U)] ? U : T;
// Instead of
type dropFirst<T extends any[]> = 
    ((...args: T) => any) extends (arg: any, ...rest: infer U) => any ? U : T;

But the same error occurs: _A rest element type must be an array type._

A minimal example showing the non-symmetricity of function arguments vs. tuple types.

declare function foo<T0, T extends any[]>(a: T0, ...b: T); // works
type foo<T0, T extends any[]> = [T0, ...T]; // rest element type must be an array type

I think this should be supported and it seems as an oversight to me that it isn't in 3.1 already.
Is there a specific reason for it to be disallowed, or is it just that it wasn't top priority?


Also, it took me quite a long time to find this issue, so I'm adding a few keywords.
Keywords: spread a generic tuple in a tuple type, generic tuple rest parameter in a tuple type, spreading array type in type declaration, T extends any[] does not work in tuple literal

I would like to extend this issue for code looking like the following:

function meh<T>(...args: Extract<T, any[]>) {} // works
type Meh<T> = [...Extract<T, any[]>] // does not work (bug?)

Hopefully this all boils down to the same actual bug.

This still seems to be an issue. I have the simple case:

type Combine<T, U> = U extends unknown[] ? [T, ...U] : never;

Which fails currently because it can't determine that U is valid as a rest parameter.

This is now implemented in #39094.

Was this page helpful?
0 / 5 - 0 ratings