Home > database >  typescript conditional typed become union type
typescript conditional typed become union type

Time:06-11

try to descript a type for filling data of form item.

value's type depends on type of form item.

enum AntdInputType {
  InputText = 'InputText',
  DatePicker = 'DatePicker',
  TreeSelect = 'TreeSelect',
  // ...
}

type ValueOfTypeMap = {
  [AntdInputType.InputText]: string,
  [AntdInputType.DatePicker]: string | Date,
  [AntdInputType.TreeSelect]: string[],
  // ...
}

// value's type related to 'type' property
type FillFormItem<Type extends keyof ValueOfTypeMap> = {
  label: string
  type: Type
  value: ValueOfTypeMap[Type]
}

// it can check the type of value correctly
const fill: FillFormItem<AntdInputType.InputText> = {
  label: 'test',
  type: AntdInputType.InputText,
  value: 'word',
  // this will cause error for good
  // value: ['word']
}

// but for array of FillFormItem will not work
const fillValues: FillFormItem<keyof ValueOfTypeMap>[] = [
  {
    label: 'text',
    type: AntdInputType.InputText,
    // here will pass, because value's type become unions type (string | Date | string[])
    value: ['text'],
  }
]

I think i use the wrong way to define "array of FillFormItem"

here's the playground

CodePudding user response:

You need to create a union of all allowed states. See here:

enum AntdInputType {
  InputText = 'InputText',
  DatePicker = 'DatePicker',
  TreeSelect = 'TreeSelect',
  // ...
}

type ValueOfTypeMap = {
  [AntdInputType.InputText]: string,
  [AntdInputType.DatePicker]: string | Date,
  [AntdInputType.TreeSelect]: string[],

}

type FillFormItem<Type extends keyof ValueOfTypeMap> = {
  label: string
  type: Type
  value: ValueOfTypeMap[Type]
}

type MappedUnion<
  Keys extends string,
  KeyValues extends Record<Keys, unknown>
  > = {
    [Prop in Keys]: {
      label: string,
      type: Prop,
      value: KeyValues[Prop]
    }
  }[Keys]

type Union = MappedUnion<AntdInputType, ValueOfTypeMap>

const fill: Union = {
  label: 'text',
  type: AntdInputType.InputText,
  value: 'word',

}

const fillValues: Union[] = [
  {
    label: 'text',
    type: AntdInputType.TreeSelect,
    value: ['text',]
  }
]

Playground

Union is a discriminated union. Treat property type as discriminator. Once you provide allowed value for type - TS knows all other properties and is able to validate them

  • Related