Home > other >  Conditional type based on union string with typescript
Conditional type based on union string with typescript

Time:09-11

Lets say I have the following interfaces:

interface IPargraph {
    id: number,
    value: string
}

interface ITask {
    id: number,
    date: Date
}

and the following union type:

export type BodyTypes = "PARAGRAPH" | "TASK" | "IMAGE"

how would I make it so the data property in the following interface will be either ITask or IParagraph based on the value of type

export interface INoteBody {
  type: BodyTypes
  data: **ITask or IParagraph based on which BodyTypes is used**
}

For example, this shouldn't be valid:

const note: INoteBody = {
    type: "PARAGRAPH",
    data: {
        id: 1,
        date: new Date()
    }
}

CodePudding user response:

Try this:

interface IPargraph {
    id: number
    value: string
}

interface ITask {
    id: number
    date: Date
}

export type BodyMap = {
  "PARAGRAPH": IPargraph
  "TASK": ITask
  "IMAGE": any
}

type BodyType = keyof BodyMap

export interface INoteBody<T extends BodyType> {
  type: T
  data: BodyMap[T]
}

const date: INoteBody<"TASK"> = {
  type: "TASK",
  data: {
    id: 2,
    date: new Date(),
  },
}

const paragraph: INoteBody<"PARAGRAPH"> = {
    type: "PARAGRAPH",
    data: {
        id: 1,
        value: 'any value',
    },
}

It is also possible to change from interface to type, to avoid the explicit Generic:

type INoteGeneric<T> = T extends keyof BodyMap ? {
    type: T;
    data: BodyMap[T];
} : never;

type INote = INoteGeneric<keyof BodyMap>;

const dateWithType: INote = {
  type: "TASK",
  data: {
    id: 2,
    date: new Date(),
  },
}

const paragraphWithType: INote = {
    type: "PARAGRAPH",
    data: {
        id: 1,
        value: 'any value',
    },
}

Live test

  • Related