Skip to content

Instantly share code, notes, and snippets.

@jet2jet
Last active October 15, 2020 12:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jet2jet/e51069feb975da2f19eb66d8aee50464 to your computer and use it in GitHub Desktop.
Save jet2jet/e51069feb975da2f19eb66d8aee50464 to your computer and use it in GitHub Desktop.
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;
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,""]}] }`);
// 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