Class Structure
enum VehicleType {
car = 'CAR',
ship = 'SHIP',
}
interface BaseMoveDto {
type: VehicleType;
}
interface CarMoveDto extends BaseMoveDto {
type: VehicleType.car;
}
interface ShipMoveDto extends BaseMoveDto {
type: VehicleType.ship;
}
type VehicleMoveDto = CarMoveDto | ShipMoveDto;
abstract class Vehicle {
abstract move(dto: VehicleMoveDto): VehicleMoveDto;
}
class Car extends Vehicle {
move(dto: CarMoveDto) {
return dto;
}
}
class Ship extends Vehicle {
move(dto: ShipMoveDto) {
return dto;
}
}
Implementation Overview
class VehicleService {
getVehicle(type: VehicleType) {
switch (type) {
case VehicleType.car:
return new Car();
case VehicleType.ship:
return new Ship();
default:
throw new Error('invalid vehicle type');
}
}
calculateMovement(dto: VehicleMoveDto) {
const vehicle = this.getVehicle(dto.type);
return vehicle.move(dto);
}
}
Encountered Issue
Argument of type 'VehicleMoveDto' is not assignable to parameter of type 'never'.
The intersection 'ShipMoveDto & CarMoveDto' was reduced to 'never' because property 'type' has conflicting types in some constituents.
Type 'CarMoveDto' is not assignable to type 'never'.
https://i.sstatic.net/jlx0J.png
The error message indicates that Typescript is intersecting move() arguments instead of using union, resulting in the dto argument becoming type never and causing an error. Is there anything I may have misunderstood about typescript? Are there any solutions available to achieve the desired outcome?
Alternative Approaches
I understand that I can implement it without encountering any issues.
class VehicleService {
calculateMovement(dto: VehicleMoveDto) {
switch (dto.type) {
case VehicleType.car:
return new Car().move(dto);
case VehicleType.ship:
return new Ship().move(dto);
default:
throw new Error('invalid vehicle type');
}
}
otherMethod(type: VehicleType) {
// another method utilizing a switch case
}
}
However, with the above implementation, I am unable to reuse the switch case for other methods.