How to handle types when iteratively enriching data on the frontend?
I have a problem where I get a certain data model from an API and then restructure and enrich it further on the frontend. Until now, I've made separate, strict types/interfaces for each step of the process, but I now think there might be a better approach if I had a single type where iteratively added properties are optional.
Example:
GET /api/dogs --> returns APIDog[]
addCollarToDog(dog: APIDog): DogWithCollar
prepareDogForWalk(dog: DogWithCollar): WalkableDog
walkDog(dog: WalkableDog): WalkableDog
removeLeash(dog: WalkableDog): WalkableDog
Where
interface APIDog {
name: string
}
interface DogWithCollar extends APIDog {
collar: Collar
}
interface WalkableDog extends DogWithCollar {
leash: Leashed | undefined
knownCommand: 'stay' | 'sit'
}
This works, and the interfaces are nice and strict without any optional types, but I was wondering if it made sense to structure it like this instead in order to have fewer interfaces for essentially the same thing:
GET /api/dogs --> returns APIDog[]
addCollarToDog(dog: APIDog): UIDog
prepareDogForWalk(dog: UIDog): UIDog
walkDog(dog: UIDog): UIDog
removeLeash(dog: UIDog): UIDog
Where
interface APIDog {
name: string
}
interface UIDog {
collar?: Collar
leash?: Leash
knownCommand?: 'stay' | 'sit'
}
The latter has simpler interfaces, but if I, for example, need to access the collar during prepareDogForWalk, it is possibly undefined, or if I need to access leash or knownCommand during walkDog, they are possibly undefined as well.
The first has more complicated interfaces, the second needs checking for undefined on nearly every line. Which one would you prefer or is there another approach?I was also thinking about casting the results of the functions, with utility types, into ones where certain properties are required, but I always cringe a bit when I have to use the 'as' keyword.
do you know?
how many words do you know