Home > Software engineering >  How to conditionally type a key depending on another key of same object
How to conditionally type a key depending on another key of same object

Time:11-13

I have tried to create a type that changes depending on which enum's value is being used:

enum StatusEnum {
  UPDATE = 'update',
  CREATE = 'create'
}

type LogKeyType<K extends StatusEnum> =
  K extends StatusEnum.UPDATE ? Record<string, any> : undefined

type Props = {
  status: StatusEnum
  log: LogKeyType<status> // <-- here is the key that should change type depending on status
  // other keys...
}

For this line : log: LogKeyType<status> I get an error saying that status refers as a value but is being used as a type and I did not manage to get pass this error.

I've then tried to to use generic type too :

enum StatusEnum {
  UPDATE = 'update',
  CREATE = 'create'
}

type LogKeyType<K extends StatusEnum> =
  K extends StatusEnum.UPDATE ? Record<string, any> : undefined

type Props<K extends StatusEnum> = {
  status: K
  log: LogKeyType<K> 
  // other keys...
}

but typescript can't tell what's the type of log.

This is to be used in this use case :

if (props.status === StatusEnum.UPDATE) {
  // do something with props.log that won't be undefined
}

I could also check directly if log is undefined or not but it wouldn't be as fancy.

EDIT:

Here is the error I'm talking about in my comment from Baka's answer : playground link

CodePudding user response:

What you're looking for is discriminated union

Here's an example of how you can use it with your enum:

import React from 'react';

enum StatusEnum {
  UPDATE = 'update',
  CREATE = 'create'
}

interface BaseProps {
  status: StatusEnum;
  // other keys...
  someArbitraryKey: string;
}

interface Update extends BaseProps {
  status: StatusEnum.UPDATE;
  log: Record<string, any>;
}

interface Create extends BaseProps {
  status: StatusEnum.CREATE;
}

type Props = Update | Create;

export const Component: React.FC<Props> = props => {
  if (props.status === StatusEnum.UPDATE) {
    return <div>{props.log}</div>; // no type error
  }

  if (props.someArbitraryKey.length) { // someArbitraryKey is always available
    return <div>{props.log}</div>; // error, undefined log
  }

  return <div />;
}

TS Playground

  • Related