Last active
October 15, 2020 12:25
-
-
Save jet2jet/e51069feb975da2f19eb66d8aee50464 to your computer and use it in GitHub Desktop.
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 _Cast<T, U> = T extends U ? T : U; | |
type Spaces1 = ' ' | '\r' | '\n'; | |
type Spaces2 = `${Spaces1}${Spaces1}`; | |
type Spaces3 = `${Spaces2}${Spaces1}`; | |
type Spaces4 = `${Spaces3}${Spaces1}`; | |
type Trim<T> = | |
_Cast<T, string> extends `${Spaces1} ${infer Rest}` ? Trim<Rest> : | |
_Cast<T, string> extends `${Spaces1} ${infer Rest}` ? Trim<Rest> : | |
_Cast<T, string> extends `${Spaces1} ${infer Rest}` ? Trim<Rest> : | |
_Cast<T, string> extends `${Spaces1} ${infer Rest}` ? Trim<Rest> : | |
_Cast<T, string> extends `${Spaces1} ${infer Rest}` ? Trim<Rest> : | |
_Cast<T, string> extends `${Spaces1} ${infer Rest}` ? Trim<Rest> : | |
_Cast<T, string> extends `${Spaces1} ${infer Rest}` ? Trim<Rest> : | |
_Cast<T, string> extends `${Spaces1} ${infer Rest}` ? Trim<Rest> : | |
_Cast<T, string> extends `${Spaces4}${infer Rest}` ? Trim<Rest> : | |
_Cast<T, string> extends `${Spaces3}${infer Rest}` ? Trim<Rest> : | |
_Cast<T, string> extends `${Spaces2}${infer Rest}` ? Trim<Rest> : | |
_Cast<T, string> extends `${Spaces1}${infer Rest}` ? Trim<Rest> : | |
_Cast<T, string>; | |
type ValueNull = { __type: 'null' }; | |
type ValueTrue = { __type: 'true' }; | |
type ValueFalse = { __type: 'false' }; | |
type ValueString<TValue extends string> = { __type: 'string', __value: TValue }; | |
type ValueNumber<TValue> = { __type: 'number', __value: TValue }; | |
type ValueArray<TValue> = { __type: 'array', __value: TValue }; | |
type ValueObject<TValue> = { __type: 'object', __value: TValue }; | |
type ValueTypes = ValueNull | ValueTrue | ValueFalse | | |
ValueString<any> | ValueNumber<unknown> | ValueArray<unknown> | ValueObject<unknown>; | |
type ParseNullLiteral<T> = T extends `null${infer R}` ? [ValueNull, R] : []; | |
type ParseTrueLiteral<T> = T extends `true${infer R}` ? [ValueTrue, R] : []; | |
type ParseFalseLiteral<T> = T extends `false${infer R}` ? [ValueFalse, R] : []; | |
// type _ParseNumberDigits<T extends string, TParsed extends string = ''> = | |
// T extends `${_CharsDigit}${infer U}` | |
// ? T extends `${infer S}${U}` | |
// ? _ParseNumberDigits<U, `${TParsed}${S}`> | |
// : [] | |
// : [TParsed, T]; | |
type _ParseNumberDigits<T, TParsed extends string = '', TIsFirst extends boolean = false> = | |
T extends `9${infer U}` ? _ParseNumberDigits<U, `${TParsed}9`> : | |
T extends `8${infer U}` ? _ParseNumberDigits<U, `${TParsed}8`> : | |
T extends `7${infer U}` ? _ParseNumberDigits<U, `${TParsed}7`> : | |
T extends `6${infer U}` ? _ParseNumberDigits<U, `${TParsed}6`> : | |
T extends `5${infer U}` ? _ParseNumberDigits<U, `${TParsed}5`> : | |
T extends `4${infer U}` ? _ParseNumberDigits<U, `${TParsed}4`> : | |
T extends `3${infer U}` ? _ParseNumberDigits<U, `${TParsed}3`> : | |
T extends `2${infer U}` ? _ParseNumberDigits<U, `${TParsed}2`> : | |
T extends `1${infer U}` ? _ParseNumberDigits<U, `${TParsed}1`> : | |
T extends `0${infer U}` | |
? TIsFirst extends false | |
? _ParseNumberDigits<U, `${TParsed}0`> | |
: [`${TParsed}0`, U] | |
: TIsFirst extends true | |
? [] | |
: [TParsed, T]; | |
type _ParseNumberInteger<T> = | |
T extends `-${infer U}` | |
? _ParseNumberDigits<U, '-', true> | |
: _ParseNumberDigits<T, '', true>; | |
type _ParseNumberFraction<T> = | |
T extends `.${infer U}` ? _ParseNumberDigits<U, '.'> : ['', T]; | |
type _ParseNumberExponent<T> = | |
T extends `${'e' | 'E'}${'-' | '+'}${infer U}` | |
? T extends `${infer S}${U}` ? _ParseNumberDigits<U, S> : [] | |
: ['', T]; | |
type ParseNumberValue<T> = | |
_ParseNumberInteger<T> extends [infer I, infer U] | |
? _ParseNumberFraction<U> extends [infer F, infer V] | |
? _ParseNumberExponent<V> extends [infer E, infer W] | |
? [ValueNumber<`${_Cast<I, string>}${_Cast<F, string>}${_Cast<E, string>}`>, W] | |
: [] | |
: [] | |
: []; | |
type _ParseStringInner<T, TParsed extends string = ''> = | |
T extends `"${infer A}"${infer Rest}` | |
? A extends `${infer X}\\` | |
? _ParseStringInner<`"${Rest}`, `${X}\\"`> | |
: [`${TParsed}${A}`, Rest] | |
: []; | |
type ParseStringValue<T> = _ParseStringInner<T> extends [infer S, infer R] | |
? [ValueString<_Cast<S, string>>, R] : []; | |
// 'members', 'member' | |
type _ParseMembersInner<T> = | |
_ParseStringInner<Trim<T>> extends [infer K, infer R] | |
? Trim<R> extends `:${infer S}` | |
? ParseElement<S> extends [infer V, infer RR] | |
? RR extends `,${infer SS}` | |
? [{ [P in _Cast<K, string>]: V } & _ParseMembersInner<SS>[0], _ParseMembersInner<SS>[1]] | |
: [{ [P in _Cast<K, string>]: V }, RR] | |
: [never, ''] | |
: [never, ''] | |
: [never, '']; | |
type ParseMembers<T> = _ParseMembersInner<T> extends [never, any] | |
? [] | |
: _ParseMembersInner<T>; | |
type ParseObjectValue<T> = | |
T extends `{${infer U}` | |
? Trim<U> extends `}${infer R}` | |
? [ValueObject<{}>, R] | |
: ParseMembers<U> extends [infer O, `}${infer RR}`] | |
? [ValueObject<{ [P in keyof O]: O[P] }>, RR] | |
: [] | |
: []; | |
type _ParseElementsInner<T> = | |
ParseElement<T> extends [infer E, infer R] | |
? R extends `,${infer S}` | |
? [[E, ..._ParseElementsInner<S>[0]], _ParseElementsInner<S>[1]] | |
: [[E], R] | |
: [never, ''] | |
type ParseElements<T> = _ParseElementsInner<T> extends [never, any] | |
? [] | |
: _ParseElementsInner<T>; | |
type ParseArrayValue<T> = | |
T extends `[${infer U}` | |
? Trim<U> extends `]${infer R}` | |
? [ValueArray<[]>, R] | |
: ParseElements<U> extends [infer O, `]${infer RR}`] | |
? [ValueArray<O>, RR] | |
: [] | |
: []; | |
type ParseValue<T> = | |
ParseObjectValue<T> extends [infer V, infer R] ? [V, R] : | |
ParseArrayValue<T> extends [infer V, infer R] ? [V, R] : | |
ParseStringValue<T> extends [infer V, infer R] ? [V, R] : | |
ParseNumberValue<T> extends [infer V, infer R] ? [V, R] : | |
ParseNullLiteral<T> extends [infer V, infer R] ? [V, R] : | |
ParseTrueLiteral<T> extends [infer V, infer R] ? [V, R] : | |
ParseFalseLiteral<T> extends [infer V, infer R] ? [V, R] : | |
[]; | |
type ParseElement<T> = | |
ParseValue<Trim<T>> extends [infer V, infer R] | |
? [V, Trim<R>] | |
: []; | |
/** Convert string literal type to parsed JSON structure type (ValueXXX types) */ | |
type ParseJSON<T extends string> = ParseElement<T> extends [infer E, ''] | |
? E : never; |
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 _TypeFromJSONStructureArray<A> = | |
any[] extends A | |
? A extends (infer E)[] ? TypeFromJSONStructure<E>[] : never | |
: A extends [infer E, ...infer R] | |
? [TypeFromJSONStructure<E>, ..._TypeFromJSONStructureArray<R>] | |
: []; | |
/** Convert parsed JSON structure type to actual type */ | |
type TypeFromJSONStructure<T> = | |
T extends ValueObject<infer O> ? | |
{ [P in keyof O]: TypeFromJSONStructure<O[P]> } : | |
T extends ValueArray<infer A> ? | |
_TypeFromJSONStructureArray<A> : | |
T extends ValueString<any> ? string : | |
T extends ValueNumber<any> ? number : | |
T extends (ValueTrue | ValueFalse) ? boolean : | |
T extends ValueNull ? null : | |
never; | |
// extends JSON.parse to narrow return type from string literal type | |
interface JSON { | |
parse< | |
T extends string, | |
TParsed extends ParseJSON<T> | |
>(json: string extends T ? never : T): TypeFromJSONStructure<TParsed>; | |
} | |
// type of p is { a: number; b: [number, { c: [number, number, number, number, number]; }]; } | |
const p = JSON.parse(`{ "a": -1, "b": [-2, {"c": [1,2,3,-4,5]}] }`); | |
// error (TS2589) | |
const q = JSON.parse(`{ "a": -1, "b": [-2, {"c": [1,2,3,-4,5,""]}] }`); |
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
// NOTE: not fully implemented for JSON schema | |
type Flatten<O extends object> = { [P in keyof O]: O[P] }; | |
type FindFieldFromObject<T, TMember extends string> = | |
T extends ValueObject<infer O> | |
? O extends { [P in TMember]: infer V } | |
? V | |
: never | |
: never; | |
type ValueIsObject<T extends ValueTypes> = | |
T extends ValueObject<infer O> ? O : never; | |
type MakeOptionalWithoutKeys<O extends object, K extends string> = | |
{ [P in Extract<keyof O, K>]: O[P] } & | |
{ [P in Exclude<keyof O, K>]?: O[P] }; | |
type ObjectDefinitionToObject<T> = | |
T extends { properties: infer P } | |
? P extends ValueObject<infer O> | |
? T extends { required: infer R } | |
? R extends ValueArray<infer A> | |
? A extends ValueString<infer S>[] | |
? Flatten<MakeOptionalWithoutKeys<{ [P in keyof O]: TypeFieldToType<O[P]> }, S>> | |
: never | |
: never | |
: { [P in keyof O]: TypeFieldToType<O[P]> } | |
: never | |
: {} | |
type TypeLiteralToType<S, T> = | |
S extends 'object' ? ObjectDefinitionToObject<T> : | |
S extends 'number' ? number : | |
S extends 'string' ? string : | |
S extends 'date-time' ? string : | |
S extends 'time' ? string : | |
S extends 'date' ? string : | |
S extends 'email' ? string : | |
S extends 'idn-email' ? string : | |
S extends 'hostname' ? string : | |
S extends 'idn-hostname' ? string : | |
S extends 'ipv4' ? string : | |
S extends 'ipv6' ? string : | |
S extends 'uri' ? string : | |
S extends 'uri-reference' ? string : | |
S extends 'iri' ? string : | |
S extends 'iri-reference' ? string : | |
never; | |
type TypeFieldToType<T> = | |
T extends ValueObject<infer O> | |
? O extends { type: infer V } | |
? V extends ValueString<infer S> | |
? TypeLiteralToType<S, O> | |
: V extends ValueArray<infer A> | |
? A extends (ValueString<infer SS>)[] | |
? TypeLiteralToType<SS, O> | |
: never | |
: never | |
: never | |
: never; | |
// Hoge is (string | number) | |
type Hoge = TypeFieldToType<ParseJSON<'{"type": ["string", "number"]}'>>; | |
// Piyo is: | |
// { | |
// name: string; | |
// street_address: string; | |
// city: string; | |
// state: string; | |
// state2?: string | undefined; | |
// phone?: number | undefined; | |
// company?: string | undefined; | |
// } | |
type Piyo = TypeFieldToType<ParseJSON<` | |
{ | |
"type": "object", | |
"properties": { | |
"name": {"type": "string"}, | |
"street_address": {"type": "string"}, | |
"city": {"type": "string"}, | |
"state": {"type": "string"}, | |
"state2": {"type": "string"}, | |
"phone": {"type": "number"}, | |
"company": {"type": "string"} | |
}, | |
"required": ["name", "street_address", "city", "state"] | |
} | |
`>>; | |
// error (TS2589) | |
type Piyo2 = TypeFieldToType<ParseJSON<` | |
{ | |
"type": "object", | |
"properties": { | |
"name": {"type": "string"}, | |
"street_address": {"type": "string"}, | |
"city": {"type": "string"}, | |
"state": {"type": "string"}, | |
"state2": {"type": "string"}, | |
"phone": {"type": "number"}, | |
"company": {"type": "string"}, | |
"dummy1": {"type": "string"}, | |
"dummy2": {"type": "string"}, | |
"dummy3": {"type": "string"} | |
}, | |
"required": ["name", "street_address", "city", "state"] | |
} | |
`>>; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment