Home > Enterprise >  Type specificity in conditional type
Type specificity in conditional type

Time:05-25

I'm trying to type properties of the React component in a way that when one of the properties has specific value then the type of the other property changes. Overall, I know how to do it if the input type is something like string | number but I don't know how to do it if it's string | "specificString". Maybe it's not even possible?

Here are my two attempts to achieve that.

type Action = "createItem" | "updateItem" | "deleteItem";

type Props<TName = string> = {
    name: TName;
    value: TName extends "_action" ? Action : string;
};

const props: Props = {
    name: "_action",
    value: "wrongAction", // I want it to error
};

Second try:

type Action = "createItem" | "updateItem" | "deleteItem";

type Props = {
    name: "_action";
    value: Action;
} | {
    name: string;
    value: string;
};

const props: Props = {
    name: "_action",
    value: "wrongAction", // I want it to error
};

Playground link

Any ideas how to achieve that? Thanks!

CodePudding user response:

See this example:

import React from 'react'

type Action = "createItem" | "updateItem" | "deleteItem";

type ValidateProps<T> =
  /**
   * Check whether our argument has allowed type shape
   */
  T extends { name: infer A, value: infer V }
  /**
   * Check if [name] is "_action"
   */
  ? ('_action' extends A
    /**
     * If [name] is "_action" and [value] is Action
     */
    ? (V extends Action
      /**
       * Return T
       */
      ? T
      /**
       * Otherwise highlight wrong property
       */
      : { name: A, value: 'Error: [value] is not allowed' })
    : { name: A, value: V }
  )
  : never

type Props<Name, Value> = {
  name: Name,
  value: Value
}

const Foo = <
  Name extends string,
  Value extends string,
  >(props: ValidateProps<Props<Name, Value>>) => <div />

const allowed1 = <Foo name="_action" value="createItem" /> // ok
const allowed2 = <Foo name="anything" value="hello" /> // ok
const not_allowed = <Foo name="_action" value="hello" /> // expected error

Playground

You need to infer literal type from name and value property and them validate it.

If you are interested in argument type inference, you can check my articles here and here

P.S. Feel free to use never instead of 'Error: [value] is not allowed'. I just added it for the sake of readability

  • Related