I expect all three functions to be compiled successfully.
TypeScript doesn't allow using ReadonlyArray although it is the same as Array but readonly.
function f(...rest: any[]) { }
function g(...rest: Array<any>) { }
function h(...rest: ReadonlyArray<any>) { }
This limitation makes harder to use tslint-immutable package.
https://www.npmjs.com/package/tslint-immutable
Is the following the right place to start with implementing a fix?
There are a few places where we make an assumption about rest params being the globalArrayType
, we will need to look at them all and fix them. one place is for example any use of isArrayType
with rest params.
Union and intersections are another example (See https://github.com/Microsoft/TypeScript/issues/23440).
function dist(x1: number, y1: number, x2: number, y2: number): number
function dist(point1: point, point2: point): number
function dist(...args: number[] | point[]): number {
// function body
}
I think I'm hitting the same issue when I do something like this:
declare interface R {
a: [string, number],
b: [boolean]
}
declare function on<T extends keyof R, K extends R[T]>(val: T, listener: (...args: K) => void): void;
// rest parameter must be array type ^ in ...args
on('b', (a) => {
})
as a workaround I've had to not use the splat args, and overload the method for each arity
This is also an issue for conditional types inside generic class methods
class Foo <T> {
bar(...args: T extends number ? number[] : string[]): void { // A rest parameter must be of an array type.
}
}
Yeah I've just run into this issue...
remove(...items: (M[] | number[])) {}
The above code should work, but does not :(
There is a temporary work around for some of the issues mentioned here, specifically the union types, until problem is resolved
type F1 = (...args: number[] | string[]) => any; // Throws an error
type Fof<T extends any[], R> = (...args: T) => R;
type F2 = Fof<number[] | string[], any>; // Compiles without error
It's not clear to me why this fix works. As you can see in the screenshot below, the type of F2
is (...args: number[] | string[]) => any
even though that type is apparently an error.
type Fof<T extends any[], R> = (...args: A) => R;
You meant 'T' and not 'A' I guess
ts type Fof<T extends any[], R> = (...args: T) => R;
Ya
type Fof<T extends any[], R> = (...args: A) => R;
You meant 'T' and not 'A' I guesstype Fof<T extends any[], R> = (...args: T) => R;
Ya I did thanks for pointing that out :+1:
I have another (albeit slightly dysfunctional) use case impacted by this. Using Typescript 3.1.0-rc.
class Pipeline<PS extends any[]> {
// Gives error:
//
// A rest element type must be an array type.
//
// NOTE: Technically incorrect because `PS` includes `PS[0]`
//
public constructor(fns: [HeadFn<PS[0]>, ...TailFns<PS>]) {
}
}
interface HeadFn<T> {
kind: 'head'
props: T
}
interface TailFn<T> {
kind: 'tail'
props: T
}
type TailFns<TS> = {
[K in keyof TS]: TailFn<TS[K]>
}
Same error with a stronger restriction on the TailFns
type parameter:
type TailFns<TS extends any[]> = {
[K in keyof TS]: TailFn<TS[K]>
}
Of note, this is also an issue with tuple splat types in typescript > 3.0.
i.e.
// works
function tuple<T extends any[]>(...value: T) {
return value
}
// compile error
function tuple<T extends ReadonlyArray<any>>(...value: T) {
return value
}
... same for TS 3.0 "Rest elements in tuple types":
type MyTuple1 = [string, ...string[]];
// Error in TS 3.0/3.1:
type MyTuple2 = [string, ...ReadonlyArray<string>];
(I didn't find an own issue for that.)
Subscribing for this
For those that land here like I did and think they need to use the Fof<> workaround above, in my case it was much easier. I had this:
add: (...items: T[] | Enum<T>[]) => void;
Which I just needed to change to:
add: (...items: (T | Enum<T>)[]) => void;
Might be obvious to all but noobs, but since this was the first hit on Google when I searched on the error message, safe to say I'm not the only noob reading this.
@ericselkpc It may not matter terribly much, but I'd just like to point out that this solution has one technical problem - it allows mixed arrays... ie. an array that contains items of both type T and type Enum
@ericselkpc your first example now works in 3.3, my example above also now works.
Looks like this issue was fixed in https://github.com/Microsoft/TypeScript/pull/29435
See https://github.com/Microsoft/TypeScript/pull/29435#issuecomment-454990968
/cc @ahejlsberg @DanielRosenwasser
Most helpful comment
This limitation makes harder to use tslint-immutable package.
https://www.npmjs.com/package/tslint-immutable