2

I'm trying to get the type/class from an custom object instance. This will then be used as a type parameter passed to a generic function rather than just use the 'any' type.

I've tried foo.prototype, Object.getPrototypeOf(foo), typeof foo, foo.constructor, foo.constructor.name etc but haven't yet found a way to return the objects's type/class itself. The example below gives an idea of what I want to achieve - it doesn't work here because constructor.name only returns the name of the type:

var vehicle 

if (selected == 'car') { 
vehicle = new Car() 
} else { 
vehicle = new Bike()  
} 

var vehicleType = vehicle.constructor.name

buildVehicle<vehicleType>(vehicle) 

buildVehicle<T>(vehicle:T) { 
    do stuff… 
}

I'm pretty new to typescript and javascript so I'm not sure this is even possible, but hopefully there is a way to do this.

Thanks for any help on this.

TeMo
  • 23
  • 3
  • Possible duplicate of [Get an object's class name at runtime](https://stackoverflow.com/questions/13613524/get-an-objects-class-name-at-runtime) – Michał Tkaczyk Jul 23 '19 at 21:20
  • Typescript types don't exist at runtime, so there's no way you'll be able to get `vehicleType` out of an object instance and then pass this as a type parameter to `buildVehicle(vehicle)`. If you think about it, `buildVehicle(vehicle)` requires a type at *compile-time*, but if your code did work you'd only be able to get that type at *run-time*. – Collierre Jul 23 '19 at 21:34
  • @MichałTkaczyk - that link only gets the name - I want to get the type itself – TeMo Jul 24 '19 at 11:19
  • @Collierre - I thought that was only a limitation if I wanted to get an interface (ie you can get classes but not interfaces at runtime). I know if I explicitly put `buildVehicle(vehicle)` it will work fine - I just don't know how I can get a reference to Car (or Bike) from the instance. – TeMo Jul 24 '19 at 11:30
  • @TeMo, typescript is javascript at runtime, and in javascript at runtime you cannot get a class from one of its instances (though you can get the class name). Therefore you cannot in typescript either. – Collierre Jul 24 '19 at 12:46

2 Answers2

1

In your example you shouldn't add the type to the generic at all. TypeScript can infer the type in this context, because T is used a parameter.

// Use it like this: 
buildVehicle(vehicle);

If you really want to add the generic here, you could use typeof.

buildVehicle<typeof vehicle>(vehicle);

This is, in this case, equivalent to

buildVehicle<Car | Bike>(vehicle);

because it is not possible to determine which type vehicle actually has at compile time. It could only be determined at runtime. But since TypeScript types are lost during transpilation (as it transpiles to JavaScript), there is no way to do that with your current code.

You could, however, change your code to make it determinable at compile time.

if (vehicle instanceof Car) {
   buildVehicle<Car>(vehicle); // Vehicle is narrowed down to type "Car"
} else if (vehicle instanceof Bike) { // else would suffice if only 2 possible types
   buildVehicle<Bike>(vehicle); // Vehicle is narrowed down to type "Bike"
}

Though, in your case, this would probably not make much sense.

pascalpuetz
  • 5,238
  • 1
  • 13
  • 26
  • Not sure about that - the type might be inferred correctly, but I think I should be able to pass in a type parameter to make this explicit. – TeMo Jul 24 '19 at 11:40
  • If you want to add the type, it needs to be a union type as the other answer stated. Since the type can be dynamically changed at runtime, the variable is of type `Bike | Car`. Since the generic information is lost at runtime, you can't do much about it. – pascalpuetz Jul 24 '19 at 13:33
  • Thanks for clarifying. Accepting this as the answer as it seems to cover all the options including use `instanceof` to get the exact type at compile time. I will go with the more compact union type `Bike|Car` method for now and switch to the more verbose conditional checks on `instanceOf` if I need the extra precision. Thanks to all contributors who helped with this (I have upvoted all answers independently). – TeMo Jul 24 '19 at 20:16
0

Try to use type union: type Vehicle = Bike | Car;

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
stesel
  • 37
  • 3
  • Add more relevant data and make your A better – Nikola Lukic Jul 24 '19 at 06:55
  • This works and seems better than using `any`. If I cant find a way to get the exact type from the instance, I may well go with this as the next best thing. Will see if any other ideas turn up here :) – TeMo Jul 24 '19 at 12:08
  • Another alternative would be `buildVehicle(vehicle: Car|Bike) { ` – Collierre Jul 24 '19 at 12:50