0
// Generic Constraints
class Car {
  print() {
    console.log('I am a car')
  }
}
class House {
  print() {
    console.log('I am a house')
  }
}

interface Printable {
  print(): void;
}

// tell Typescript that I promise the T type will satisfy the Printable interface
function printHousesOrCars<T extends Printable>(...arr: T[]): void {
  arr.forEach(item => item.print())
}

printHousesOrCars(1, 2, 3) // This line went wrong,I can understand
printHousesOrCars(new House(), new Car()) // this line Typescript infer T[] is Car[], I cannot understand, why shouldn't it be (House|Car)[]

I cannot understand the last line, and if I wrote

const x = [new House(), new Car()] // Typescript will infer x as (House|Car)[]

5
  • Your House and Car have the same structure, so the compiler sees them as the same type. You should distinguish them at the type level, say by adding incompatible properties, like a manufacturer property to Car and a numBathrooms property to House. Commented Jan 3, 2020 at 4:01
  • What if you change your function to: function printHousesOrCars<T extends Printable[]>(...arr: T): void { //etc... ? Commented Jan 3, 2020 at 5:19
  • @jcalz Thanks,this really helps. Does it mean using genrics, TS won't infer the type as a union type, it must be a specific type. Commented Jan 3, 2020 at 6:03
  • @flavio won't work bro,then T[] is a 2D array Commented Jan 3, 2020 at 6:04
  • @crazyones110 it'll not be restricted to a 2D array. The length of the array really depends on how you call the function. If you call it with 10 parameters, it'll infer to a tuple with 10 itens. Look this playground code Commented Jan 3, 2020 at 6:15

1 Answer 1

3

The following line will be interpreted by the Typescript as a two elements tuple of type [House, Car].

const x = [new House(), new Car()] // Typescript will infer x as (House|Car)[]

It's a bit confusing, I know, because of both using the same syntax i.e the [ and ].

Now you can modify the function signature a bit in order to produce, what I believe, more correct typing.

function printHousesOrCars<T extends Printable[]>(...arr: T): void {
  arr.forEach(item => item.print())
}

On the call site the above will be resolved as a function with first arg House and a second Car.

printHousesOrCars(new House(), new Car()) // [House, Car]

Playground More on the rest parameters subject

I hope this makes sense. :)

Sign up to request clarification or add additional context in comments.

1 Comment

you mean all union type array will be interpreted as tuple? like const y = [1, true, 'hello'] . Actually it's [number, boolean, string] tuple instead of (number|boolean|string)[] when cursor hovering. Typescript treat (number|boolean|string)[] as a notation for tuple for simplicity?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.