Home > Blockchain >  Typescript conditionally making a property optional
Typescript conditionally making a property optional

Time:05-24

Suppose I have a type such as

interface Definition {
    [key: string]: {
        optional: boolean;
    }
}

Is it now possible to construct a type ValueType<T extends Definition> that, for a definition

{
    foo: { optional: true },
    bar: { optional: false }
}

outputs the type

{
    foo?: string;
    bar: string;
}

Is that possible?

My first approach would have been a construct made of a mapped type and Omit which works by merging the optional and required properties into a single object. However, this requires a union type made of all the optional keys and I am at a loss on how to do that.

The basic Idea was:

type ValueType<T extends Definition> = 
      { [key in keyof T]?: string } 
    & Omit<{ [key in keyof T]: string }, OptionalKeys<T>>

With OptionalKeys<T> returning the aforementioned union type made from all of the optional keys.

CodePudding user response:

You can get that behaviour with this logic:

type ValueType<T extends Record<string, { optional: boolean }>> = 
  (
    { [K in keyof T]?: string } &
    { [K in keyof T as T[K]["optional"] extends false ? K : never]-?: string }
  ) extends infer O ? { [K in keyof O]: O[K] } : never

We create an intersection of all keys where optional is true with ? and all keys where optional is false. The extends infer O ? {[K in keyof O]: O[K]} : never at the end makes the type "prettier" but is functionally irrelevant.

Edit:

Thanks to @jcalz. We can make it a bit shorter and also keep the order of the keys.

Playground

  • Related