3

I have a function with the following signature:

test<T>(...args: T[]): T

if I call the function like this:

const a = test(1, 2, 3)

everything works as expected (and a is of type number), but if I call it that way:

const a = test(1, 2, "asd")

I get any error: [EDIT] The 2 is red underlined: Argument of type '2' is not assignable to parameter of type '1'., witch might be a bit misleading because the arguments are constants, but if I call it that way:

let arg1 = 1;
let arg2 = "asd";
const a = test(arg1, arg2);

I get the error: Argument of type 'string' is not assignable to parameter of type 'number'.

How can I make it so that the function would (in the second case) has a return type of number | string without explicitly specifying it in the generic parameter.

Hexception
  • 722
  • 10
  • 25
  • What does "I get any error" mean? Assuming you mean "an error", please consider editing the post to include the text of this error and where it occurs. – jcalz Jun 04 '21 at 18:39

2 Answers2

1

I think this is the call signature you are looking for:

<T extends Array<unknown>>(...args: T): T[number]

TypeScript Playground

evelynhathaway
  • 1,670
  • 14
  • 18
1

It is intentional that T is not inferred as a union; see Why isn't the type argument inferred as a union type?, as well as microsoft/TypeScript#19596 and microsoft/TypeScript#26746 for reasons why.

Probably the easiest way around this is to allow args to be any array type T whatsoever, and then get its element type by indexing into it with the key type number:

declare function test<T extends any[]>(...args: T): T[number];
const a = test(1, 2, 3) // number
const b = test(1, 2, "asd") // number | string

Playground link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360