Typescript Conditional Types Confusion

70 views Asked by At

In this code:

const schemas = {
  foo: {
    date1: {
      display: 'date',
      type: 'date',
    },
    text1: {
      display: 'text',
      type: 'text',
    },
    number1: {
      display: 'number',
      type: 'number',
    },
  },
  bar: {
    date2: {
      display: 'date',
      type: 'date'
    },
    text2: {
      display: 'text',
      type: 'text'
    },
    number2: {
      display: 'number',
      type: 'number'
    }
  },
} as const
    
function schema(schemaType: keyof typeof schemas) {
  const fields = schemas[schemaType]
  
  type NumericColumnKeys = {
    [K in keyof typeof fields]: (typeof fields)[K] extends {
      type: 'number'
    }
      ? K
      : never
  }[keyof typeof fields]
}

Plaground Example

My problem is that 'NumericColumnKeys' resolves to never. However, if 'schemas' only has one key, it works correctly, i.e.:

    const schemas = {
      foo: {
        date1: {
          display: 'date',
          type: 'date',
        },
        text1: {
          display: 'text',
          type: 'text',
        },
        number1: {
          display: 'number',
          type: 'number',
        },
      }
    }
    *...*

Playground Example

I am trying to use this code to dynamically generate a validation schema from a configurator object. You can find a full example of what I'm trying to accomplish here: https://github.com/oshox/schema-function-test/blob/main/index.ts Please feel free to suggest better ways to do this.

I need to be able to extract all of the key names from a sub-object based on their 'type' value and be able to use that to construct a Typebox validator.

1

There are 1 answers

0
vilka27 On

You can use a conditional type with generic to force TypeScript to process each member of the Fields type individually, rather than as a union.

function schema(schemaType: keyof typeof schemas) {
 const fields = schemas[schemaType]

 type Fields = typeof fields;

 type NumericColumnKeys<T = Fields> = T extends T ? {
   [K in keyof T]: T[K] extends {
     type: 'number'
   }
     ? K
     : never
 }[keyof T] : never;

}