Created
April 18, 2023 12:26
-
-
Save mekarthedev/8ca67e04dfcee2fc32a06293015bd005 to your computer and use it in GitHub Desktop.
Hand made inference of a Typescript type from a custom json schema
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type TypeFromString<T extends string> = | |
T extends "number" ? number | |
: T extends "string" ? string | |
: ["Invalid type string", T] | |
type InferPropRequired<PropSpec> = PropSpec extends { isRequired: false } ? false : true | |
type InferPropType<PropSpec> = | |
PropSpec extends { values: ReadonlyArray<infer Values> } ? Values | |
: PropSpec extends { type: infer T extends string } ? TypeFromString<T> | |
: ["Invalid prop spec", PropSpec] | |
type InferPayload<Payload> = { | |
[Prop in keyof Payload as (InferPropRequired<Payload[Prop]> extends true ? Prop : never)]: InferPropType<Payload[Prop]> | |
} & { | |
[Prop in keyof Payload as (InferPropRequired<Payload[Prop]> extends false ? Prop : never)]+?: InferPropType<Payload[Prop]> | |
} | |
type InferSchema<Schema> = { | |
[Event in keyof Schema]: Schema[Event] extends { payload: infer P } ? InferPayload<P> : ["Invalid payload for event", Event] | |
} | |
// | |
// Simulate `import json from "schema.json" as const` | |
// See https://github.com/microsoft/TypeScript/issues/32063 | |
const json = { | |
"some_metric": { | |
"description": "Some event", | |
"payload": { | |
"index": { | |
"description": "Some index", | |
"type": "number" | |
}, | |
"card_type": { | |
"description": "Card type", | |
"type": "number", | |
"values": [0, 1] | |
}, | |
"phrase": { | |
"description": "Some phrase", | |
"type": "string", | |
"isRequired": false | |
}, | |
} | |
}, | |
"single_prop": { | |
"payload": { | |
"prop": { | |
"type": "number" | |
} | |
} | |
}, | |
"optional": { | |
"payload": { | |
"prop": { | |
"type": "number", | |
"isRequired": false, | |
} | |
} | |
}, | |
"union": { | |
"payload": { | |
"prop": { | |
"type": "number", | |
"values": [17, 23] | |
} | |
} | |
} | |
} as const | |
type Metrics = InferSchema<typeof json> | |
function sendMetric<Event extends keyof Metrics>(name: Event, payload: Metrics[Event]) { | |
// ??? | |
} | |
sendMetric("some_metric", { index: 42, card_type: 0, phrase: "asdf" }) | |
sendMetric("some_metric", { index: 42, card_type: 1 }) | |
sendMetric( | |
//@ts-expect-error "unknown_name" is not assignable to [some of known names] | |
"unknown_name", | |
{} | |
) | |
sendMetric("single_prop", | |
{ | |
//@ts-expect-error "string" is not assignable to "number" | |
prop: "not-a-number", | |
} | |
) | |
sendMetric("single_prop", | |
//@ts-expect-error "prop" is missing | |
{} | |
) | |
sendMetric("single_prop", | |
{ | |
prop: 42, | |
//@ts-expect-error may only specify known props | |
unknown_prop: 42, | |
} | |
) | |
sendMetric("optional", {}) | |
sendMetric("union", { prop: 17 }) | |
sendMetric("union", { | |
//@ts-expect-error 42 is not assignable to [some of values] | |
prop: 42 | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment