#Pattern for mapping union interfaces to classes in TypeScript

2 messages · Page 1 of 1 (latest)

verbal gazelle
#

I am looking for the most idiomatic way to handle a "Data-to-Behavior" mapping in TypeScript.

The Scenario: I receive a raw data object from a user that follows a Tagged Union pattern. Let's use a Vehicle analogy:

interface BaseVehicle {
  id: string;
}

interface CarData extends BaseVehicle {
  type: 'car';
  doors: number;
}

interface TruckData extends BaseVehicle {
  type: 'truck';
  capacity: number;
}

type VehicleData = CarData | TruckData;

The Goal: I want to "hydrate" these plain data objects into specific classes that add behavior (e.g. a .drive() method). Each subclass will implement .drive() differently based on the properties of that vehicle type. Something like this:

abstract class Vehicle<T extends BaseVehicle> {
  constructor(protected readonly data: T) {}

  abstract drive(): void;
}

The Challenges:

  • Naming Conventions: What is the best way to name the "Data" vs. the "Class"? I want the user-facing data interface to have a clean, intuitive name, but I also want my internal classes to be logically named. What is preferred in TS?
  • How do I best implement a factory that takes a VehicleData union and returns the correct class instance?

Is there maybe a different cleaner or more "TS-native" approach that I am not thinking of?

marble shell
#

what's the use case for the dehydrated versions? serialization?