1

I have the following object:

const schemas = {
  POST: {
    $schema: 'https://json-schema.org/draft/2019-09/schema#',
    $id: 'https://api.netbizup.com/v1/health/schema.json',
    type: 'object',
    properties: {
      body: {
        type: 'object',
        properties: {
          greeting: {
            type: 'boolean',
          },
        },
        additionalProperties: false,
      },
    },
    required: ['body'],
  } as const,
  PUT: {
    $schema: 'https://json-schema.org/draft/2019-09/schema#',
    $id: 'https://api.netbizup.com/v1/health/schema.json',
    type: 'object',
    properties: {
      body: {
        type: 'object',
        properties: {
          modified: {
            type: 'string',
          },
        },
        required: ['modified'],
        additionalProperties: false,
      },
    },
    required: ['body'],
  } as const,
};

I'm trying to create a type that would extract the body property type from the above object, so if:

type MyType = { body: TWhatGoesHere }

...then TWhatGoesHere would be equal to:

{ greeting?: boolean } | { modified: string }

I am using FromSchema from the json-schema-to-ts package to infer the body type from the const object above, but I'm unable to "automatically" create this type.

Sammy
  • 3,395
  • 7
  • 49
  • 95
  • You have trailing commas at "required: ['body'],", maybe this is stopping the parser? – Clemens Mar 07 '22 at 14:00
  • The parser is working very well. I'm just unable to create a utility type that would extract the above automatically instead of me doing it manually. – Sammy Mar 07 '22 at 14:03

1 Answers1

2

You don't need a utility type; it's as simple as (very long) bracket notation:

type SchemaBodies = (typeof schemas)[keyof typeof schemas]["properties"]["body"]["properties"];

The important part is keyof typeof schemas, which results in a union of all the values.

P. S. It might help to understand it if you gradually add each path in the bracket notation :)

Playground

kelsny
  • 23,009
  • 3
  • 19
  • 48
  • Thank you very much! The reason I was talking about a utility type is that I wanted to be able to "apply" this to each input schema I have in the app -- there are a lot. It's not like I can make the type a "function". No? – Sammy Mar 07 '22 at 14:38
  • You can make it a generic (`SchemaBodies = S[keyof S]...`) and use `SchemaBodies` – kelsny Mar 07 '22 at 14:48
  • Yes, that's what I tried: `type BodySchema = S[keyof S]['properties']['body'];` but I'm getting this error: `TS2536: Type '"properties"' cannot be used to index type 'S[keyof S]'.` – Sammy Mar 07 '22 at 14:53
  • I forgot the generic constraint `S extends { [key: string]: { properties: { body: { properties: unknown; } } } }` – kelsny Mar 07 '22 at 17:56
  • By the way, the issue here remains that if PUT and POST above have different attributes/properties (which is the case) the union does not work :-( – Sammy Mar 08 '22 at 09:44