Typescript: Generic type constraint does not apply to rest parameters

Created on 12 Mar 2015  路  8Comments  路  Source: microsoft/TypeScript

The following fails to compile even though T is constrained to an array type:

function attach<T extends any[]>(cb: (...args: T) => void): void { }
A rest parameter must be of an array type.
(parameter) args: T extends any[]
By Design

Most helpful comment

As a library author this makes me sad, since with overloading you lose the parameter name information. Plus it produces ugly inline documentation.

All 8 comments

This is technically by design but it's a reasonable suggestion. We also don't allow subtypes of array without generics.

Spec section 3.8.2.2

This doesn't actually make sense.

The function should be rewritten this way:

function attach<T>(cb: (...args: T[]) => void): void { }

Any production that requires that you write T instead of T[] would require a lie, because the type of the rest arg is always going to be the basic Array type, not some subtype thereof. It would be especially dangerous to flow that type through.

@RyanCavanaugh what if the function is meant to be used like the following?

attach((age: number, name: string) => {
    console.log({ age, name });
});

// written explicitly:
attach<[number, string]>(/*... */);

Using the ...args: T[] approach we would then have this typed as (number | string)[] instead of [number, string] right?

If that's the desired behavior you'd be better off writing some explicit overloads e.g. https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/underscore/underscore.d.ts#L1126

As a library author this makes me sad, since with overloading you lose the parameter name information. Plus it produces ugly inline documentation.

I'd love to see this decision reconsidered. As I understand it, one of TypeScript's major design goals is to have a type system flexible enough to represent any common JavaScript design pattern. Rest parameters have been supported for years, and generics are a vital part of TypeScript, the fact that you can't use both together is unfortunate.

I'd also like to be able to use this feature. I'm building an abstract command pattern class that I'd like to do something like this for:

abstract class Command<T extends any[]> {
    public load(...args: T);
    public loadIfNeeded(...args: T);
    public clearAndReload(...args: T);
}

Requiring implementing classes to override every method in the abstract class just to provide a type definition is ugly. Implementing this issue's feature would solve this nicely.

Was this page helpful?
0 / 5 - 0 ratings