Issue
I'm following up on a previous question:
I have the following schema:
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: 'string',
},
},
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: 'boolean',
},
},
required: ['modified'],
additionalProperties: false,
},
},
required: ['body'],
} as const,
};
I am using FromSchema
from the json-schema-to-ts
package to infer the type of each body
attribute the const
object above.
What I need to achieve is an intersection type of POST
and PUT
bodies. The answer provided in the previous question was perfect to create a TypeScript Union
. The problem is of course that when I try to access members of body
I get an error since not all attributes will always exist on each body
.
I was even wondering if TypeScript allows us to verify that if we access one attribute from a "chosen" body
object, then it won't allow us to access the other.
I created a Playground to illustrate my question.
ANNEX
Solution
It seems as if FromSchema cannot handle a union of the schemas. You can get around this by turning the union into a tuple, converting each schema in the tuple with FromSchema, then converting it back to a union.
For this we will first need the excellent holy grail of a post in How to transform union type to tuple type
I've renamed TuplifyUnion
to UnionToTuple
here.
Let us then write a type that converts a union of schemas we wish to transform.
We need to convert this union to a tuple, then convert each of the schemas separately, and finally turn the resulting tuple back into a union.
type FromAllSchemas<U> = ConvertAll<UnionToTuple<U>>[number];
This is done with UnionToTuple
and [number]
.
Now let's write ConvertAll
.
type ConvertAll<T, R extends ReadonlyArray<unknown> = []> =
T extends [infer First, ...infer Rest]
? ConvertAll<Rest, [...R, FromSchema<First>]>
: R;
First we infer the first element and the rest as a tuple.
We then convert the first element to a type with FromSchema, and put it in the results. Then we "call" ConvertAll
again, this time with the rest of the elements and new result.
Finally, if we couldn't infer the first element that means T is empty, so we just return the result R.
Answered By - kellys
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.