TypeScript: conditionally type a property based on another property

Let's say we want to model a workout plan in TypeScript.

A workout plan has:

Depending on the workout type, the list of exercises should be typed accordingly, i.e., a cardio exercise has distance and duration, while a strength training exercise has number of sets, number of reps, and weight.

How can we achieve that?

The following is one way to do it. First, we set a base type without type and list of exercises:

type WorkoutPlanBase = {
  id: number
  workoutName: string
  created_at: Date
  updated_at: Date
}

Then, we define our workout plan type using an intersection (to extend from the base type) and a discriminated union (where we use the literal type for workoutType to specify the respective two possible types for exercises):

type WorkoutPlan = WorkoutPlanBase &
  (
    | { workoutType: "strength", exercises: StrengthTrainingExercise[] }
    | { workoutType: "cardio", exercises: CardioExercise[] }
  );

This example from Blaine Garret's article was very helpful for my understanding.