Is it possible to do type casting inside an each block in svelte

Viewed 1222

I have these types:

type Exercise = {
 type: string
 prompt: string
 answer: string
}

type ComplexExercise = {
  type: string
  prompt: string,
  subExercises: Exercise[]
}

Im trying to make a component that fetches data from an api and renders it according to the type

<script lang="ts">
  let promise = getExercise(params.exerciseId).then((ex) => (exercise = ex));
  let exercise: ComplexExercise | Exercise;
</script>
...

{#await promise}
  <p>Loading exercise</p>
{:then}
  {#if exercise.type !== "COMPLEX"}
    <BaseEditor {exercise} />
  {:else}
    {#each exercise.subExercises as ex}
      <BaseEditor {ex} />
    {/each}
  {/if}
{/await}

I get the following error

Property 'subExercises' does not exist on type Exercise

Casting it throws this error

{#each (exercise as ComplexExercise).subExercises as ex}
                 ^ Unexpected token svelte(parse-error)

Only thing that seems work is setting exercise type to any, I'm avoiding it for the obvious reason. Any help is appreciated

1 Answers

Change your types to use discriminated union types. That means, there is a common property on which TS can determine which concrete type it is. In your case that property is type. Assuming you only have these two exercise types:

type Exercise = {
 type: "SIMPLE"
 prompt: string
 answer: string
}

type ComplexExercise = {
  type: "COMPLEX"
  prompt: string,
  subExercises: Exercise[]
}

If you do this, TS will know through {#if exercise.type !== "COMPLEX"} that in the else block the type is ComplexExercise and the type error will go away.

More on discriminated unions: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions

Related