Home > Blockchain >  How to infer types of an object created from a schema?
How to infer types of an object created from a schema?

Time:04-08

I'm trying to implement something similar to storybook's "Controls" feature where you can define a bunch of properties, and they become controls in the UI.

I defined a schema type and a schema of how to create those controls:

// Example schema
var newSchema: BlockSchema = {
  title: "New Schema",
  controls: {
    name: {
      type: 'string',
      placeholder: 'Please insert your name'
    },
    size: {
      type: 'select',
      options: ['quarter', 'half', 'full']
    },
    hasInfo: {
      type: 'bool'
    },
    amount: {
      type: 'number'
    }
  }
}

But now I need a type that is the result of what the user has selected. A type for the final values, something like:

type MapControlTypes = {
  bool: boolean;
  string: string;
  select: string;
  number: number;
};

type InferType<T extends BlockSchema> = { /* MapControlTypes<?????????> */ }

type NewSchemaControls = InferType<typeof newSchema>;

/* Expected result:
  NewSchemaControls = {
    name: string;
    size: string;
    hasInfo: boolean;
    amount: number;
  }
*/

I need to infer the types from the controls property of my schema, but how could I implement this inference? Here's a playground with complete example code


I tried implementing this, and this solution. But they don't work well and also only support two types.

Titian Cernicova-Dragomir's solution didn't work too. Playground, but it has a very similar problem that happened when I tried other solutions. Maybe is it because I'm not using MapControlTypes on my ControlSchema?


Solved!

CodePudding user response:

You can do this, using a mapped type, but first you need to preserve the original type of the schema. If you add a type annotation to it, then information about specific fields and types will be lost. It will just be a BlockSchema

The easiest way to do this is to omit the annotation, and use an as const assertion to make the compiler infer literal types for type.

With this extra info in hand, we can then use a mapped type to transform the schema into an object type:

type InferType<T extends BlockSchema> = {
  -readonly [P in keyof T['controls']]: MapControlTypes[T['controls'][P]['type']]
}

Playground Link

You can also use a function to create the schema, and be more selective about what gets the readonly treatment:

function buildBlockSchema<B extends BlockSchema>(b: B) {
  return b
}

Playground Link

  • Related