Giorgi Moniava
Giorgi Moniava

Reputation: 28654

Discriminated union props type not reporting error when passing discriminator dynamically

I was using following approach for passing props conditionally (when one prop depends on another prop) in react:

type CommonProps = { age: number };

type ConditionalProps =
  | {
      type: 'view' | 'add';
    }
  | {
      type: 'edit';
      initialData: number;
    };

let Test = (props: CommonProps & ConditionalProps) => {
  return <div />;
};

My goal was:

This actually worked most of the time, until value of type was dynamic. When I passed value of type like this:

 <Test
   age={9}
   type={Math.random() > 0.5 ? 'edit' : 'view'}
   initialData={9}
 />

Now it seems there is bug above, because TS isn't complaining anymore, and it can happen that type is "view" and I also pass initialData, which violates my first goal above.

What is the solution in such case? I suppose TS should report an error? If yes, how?

Upvotes: 4

Views: 1125

Answers (1)

motto
motto

Reputation: 3179

In general, Typescript ignores excess properties unless they're explicitly assigned or passed as an argument.

So, although in your example here you're passing { age: number, type: "view", initialData: number }, that satisfies the type { type: "view" | "add" }. This is similar to the way that, for instance, you might ignore irrelevant fields in data from an external source.

If however you really want to ban the extra prop in this instance, you can do so by specifying that its type should be never:

type ConditionalProps =
  | {
      type: 'view' | 'add';
      initialData?: never;
    }
  | {
      type: 'edit';
      initialData: number;
    };

Note that we specify initialData as never and also optional, so that it may (must) be omitted.

With the never type in place, Typescript will report an error unless the type prop is "edit".

Minimal repro available here

Upvotes: 2

Related Questions