Search code examples
arraystypescriptconstructorapply

[first, ...rest]: A spread argument must either have a tuple type or be passed to a rest parameter.ts(2556)


Setting

class X {
    constructor(first: string, ...rest: string[]) { }
}

new X(...["foo", "bar"])

yields the error

A spread argument must either have a tuple type or be passed to a rest parameter.ts(2556)

This is working:

new X("foo", ...["bar"])

but that's not very handy.

If I use

class X {
    constructor(...all: string[]) { }
}

instead it's working fine, so it must have something to do with me splitting into first and rest. But is there a way to make this work with split arguments?


Solution

  • The type of ["foo", "bar"] is string[]. If you can't control the type of this with as const, because you got it elsewhere, you can validate and narrow the type, for example:

    const input:string[] = ["foo", "bar"];
    
    if (!input.length<1) {
      throw new Error('Input must at least have 1 element');
    }
    const tuple: [string, ...string[]] = [input[0], ...input.slice(1)];
    }
    

    If the assignment feels silly, I feel this is way more elegant:

    const input:string[] = ["foo", "bar"];
    
    function assertAtLeast1Element(input: string[]): asserts input is [string, ...string[]] {
      if (input.length < 1) throw new Error('Input must at least have 1 element');
    }
    
    assertAtLeast1Element(input);
    
    class X {
        constructor(first: string, ...rest: string[]) { }
    }
    
    new X(...input);