Created
July 24, 2024 12:05
-
-
Save john-h-k/56a03a107bf3721816cb5d2979378ef1 to your computer and use it in GitHub Desktop.
part of my type routing for my A level coursework.
This file contains hidden or 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
| import e = require("express") | |
| import { SetOptional, Split } from "type-fest" | |
| export type AsyncFunction<V> = (...args: never[]) => PromiseLike<V> | |
| export type AsyncReturnType<V> = V extends AsyncFunction<infer U> ? U : V | |
| class QueryStringError extends Error { | |
| inner: Error | null | |
| constructor(inner: Error | null = null) { | |
| super() | |
| this.inner = inner | |
| } | |
| } | |
| class QueryArgumentMissingError extends QueryStringError { | |
| missing: string | |
| constructor(missing: string, inner: Error | null = null) { | |
| super(inner) | |
| this.missing = missing | |
| } | |
| } | |
| class QueryArgumentUnparsableError extends QueryStringError { | |
| name: string | |
| value: string | |
| type: string | |
| constructor(name: string, value: string, type: string, inner: Error | null = null) { | |
| super(inner) | |
| this.name = name | |
| this.value = value | |
| this.type = type | |
| } | |
| } | |
| class QueryParserInvalidError extends QueryStringError { | |
| name: string | |
| unknown: any | |
| constructor(name: string, unknown: any, inner: Error | null = null) { | |
| super(inner) | |
| this.name = name | |
| this.unknown = unknown | |
| } | |
| } | |
| enum QueryArgType { | |
| Date = "date", | |
| Number = "number" | |
| } | |
| type FromQueryTypeName<T> = T extends (infer U)[] ? FromQueryTypeName<U> | |
| : T extends `${infer value}${'?' | ''}` ? QueryTypes[`${keyof QueryTypes}` & value] | (T extends (value & `${QueryArgType}`) ? never : null) | |
| : T; | |
| type QueryResolverType<T> = T extends (...args) => any | |
| ? ReturnType<T> | |
| : T extends readonly (infer U)[] | Record<Identifier, infer U> ? U | |
| : FromQueryTypeName<T>; | |
| type QueryTypes = { | |
| number: number, | |
| string: string, | |
| date: Date | |
| } | |
| type Append<T, U extends string> = T extends `${infer value}` ? `${value}?` : { | |
| [K in keyof T as K extends string ? `${K}${U}` : K]: T[K] | |
| } | |
| type ArrayLiteral = [] | ({ | |
| [K in keyof readonly (string | number | boolean)[]]: (readonly (string | number | boolean)[])[K] | |
| } & { | |
| readonly 0: string | number | boolean | |
| }) | |
| type Identifier = string | number | symbol | |
| type QueryParser = Record< | |
| Identifier, | |
| | ArrayLiteral | |
| | Record<Identifier, string> | |
| | Record<Identifier, number> | |
| | keyof Append<QueryTypes, '?'> | |
| | keyof QueryTypes | |
| | ((...args) => unknown) | |
| > | |
| type ParsedQuery<T extends QueryParser> = { | |
| [K in keyof T]: QueryResolverType<T[K]> | |
| } | |
| export function ParsedQuery<T extends QueryParser>(req: Request, expected: Readonly<T>): ParsedQuery<T> { | |
| const funcMap = { | |
| "number": parseInt, | |
| "date": Date.constructor, | |
| "string": (x) => x | |
| } | |
| const result: Record<string, any> = {} | |
| for (const [k, v] of Object.entries(expected)) { | |
| const raw = req['query'][k] as string | |
| if (raw == null) { | |
| throw new QueryArgumentMissingError(k) | |
| } | |
| try { | |
| if (typeof v == "function") { | |
| result[k] = v(raw) | |
| } | |
| else if (k in funcMap) { | |
| result[k] = raw == null && /\w+?\?/.test(k) ? null : funcMap[k](raw) | |
| } | |
| else { | |
| throw new QueryParserInvalidError(k, v) | |
| } | |
| } | |
| catch (e) { | |
| if (e instanceof(QueryStringError)) { | |
| throw e | |
| } | |
| throw new QueryArgumentUnparsableError(k, raw, v as string, e) | |
| } | |
| } | |
| return result as ParsedQuery<T> | |
| } | |
| type _Shortest<T, U extends any[] = []> = [U] extends [T] | |
| ? U | |
| : _Shortest<T, [any, ...U]> | |
| type Shortest<T> = _Shortest<T> extends infer U | |
| ? U extends any[] | |
| ? Extract<T, U> | |
| : never | |
| : never | |
| type CharSplit<T> = Literal<T> extends T | |
| ? T extends `${infer First}${infer Remaining}` ? [First, ...CharSplit<Remaining>] : [] | |
| : never | |
| type CharJoin<T> = T extends [infer First, ...infer Remaining] | |
| ? First extends string ? `${First}${CharJoin<Remaining>}` | |
| : never : `` | |
| type Delimit< | |
| T, | |
| Prefix extends string, | |
| Postfix extends string, | |
| Optional extends boolean | { prefix: boolean, postfix: boolean } = false | |
| > = [T] extends [`${ | |
| Prefix | (Optional extends true | { prefix: true } ? '' : never)}${ | |
| infer T}${ | |
| Postfix | (Optional extends true | { postfix: true } ? '' : never)}`] | |
| ? CharJoin<Shortest<CharSplit<T>>> | |
| : never; | |
| type LiteralType = number | string | boolean | |
| type Literal<T> = T extends string ? string extends T ? never : T : never; | |
| type PathSegment<T = never> = [T] extends [never] | |
| ? `/${string}` | |
| : T extends `/${infer Segment}` | |
| ? T | |
| : never | |
| export type Path<T = never> = [T] extends [never] | |
| ? PathSegment | `${PathSegment}${PathSegment}` | |
| : T extends `/${infer First}${'' | `${infer Remaining}`}` | |
| ? T extends `${PathSegment<`/${First}`>}${'' | Path<`/${Remaining}`>}` ? T : never | |
| : never | |
| type PathParamPair<T> = T extends `:${infer id}[${infer type}]${infer opt}` | |
| ? [id, type extends keyof QueryTypes ? QueryTypes[type] : never, opt extends '?' ? id : never] | |
| : T extends `:${infer id}${infer opt}` ? [id, string, opt extends '?' ? id : never] | |
| : never | |
| type PathParamsAllRequired<T> = T extends Path<T> ? { | |
| [K in Split<T, '/'>[number] as PathParamPair<K>[0]]: PathParamPair<K>[1] | |
| } : never | |
| export type PathParams<T> = SetOptional< | |
| PathParamsAllRequired<T>, | |
| PathParamPair<Split<T & string, '/'>[number]>[2] & keyof PathParamsAllRequired<T> | |
| > extends infer U ? { [K in keyof U]: U[K] } : never; | |
| type QueryStringPair<T = never> = [T] extends [never] | |
| ? `${string}=${string}` | `${string}` | |
| : T extends `${infer Key}=${infer Value}` ? [Key, Value] : T extends `${infer Key}` ? [Key, 'string'] : never | |
| type QueryStringSegment<T = never> = [T] extends [never] | |
| ? `${'' | '?' | '&'}${QueryStringPair}` | |
| : T | |
| type QueryStringDelimiter<T> = Delimit<T, '&' | '?', '&' | '#', true> | |
| export type QueryString<T = never> = [T] extends [never] | |
| ? QueryStringSegment | QueryStringPair | |
| : T extends string | |
| ? T extends `?/${infer _}` | |
| ? never | |
| : QueryStringDelimiter<T> extends `${infer First}${'' | `&${infer Remaining}`}` | |
| ? T extends '?' | |
| ? T : QueryStringDelimiter<T> extends `${QueryStringPair<First> & never | First}${'' | QueryString<Remaining> & never | Remaining}` | |
| ? T | |
| : never | |
| : never | |
| : never | |
| type QueryStringAllRequired<T extends string> = { | |
| [K in QueryStringComponents<T>[number] as TypedQueryStringPair<K>[0]]: TypedQueryStringPair<K>[1] | |
| } | |
| type TypedQueryStringPairName<T> = T extends `${infer name}[${infer type}]${infer opt}` | |
| ? [name, QueryTypes[type & keyof QueryTypes], opt extends '?' ? name : never] | |
| : never | |
| type UntypedQueryStringPairName<T> = T extends `${infer name}?` | |
| ? [name, string, name] | |
| : T extends `${infer name}` | |
| ? [name, string, never] | |
| : never | |
| type TypedQueryStringPair<T> = TypedQueryStringPairName<T> extends never | |
| ? UntypedQueryStringPairName<T> | |
| : TypedQueryStringPairName<T> | |
| type OnlyAllow<Type, Allowed> = [Allowed] extends Type[keyof Type] ? Type : never | |
| type QueryStringComponents<T extends string> = Split<QueryStringDelimiter<T>, '&'> | |
| export type QueryParams<T = string | Record<string, unknown>> = T extends string | |
| ? SetOptional< | |
| QueryStringAllRequired<T>, keyof QueryStringAllRequired<T> & TypedQueryStringPair<QueryStringComponents<T>[number]>[2] | |
| > extends infer U | |
| ? {[K in keyof U]: U[K] } | |
| : never | |
| : OnlyAllow<T, QueryTypes[keyof QueryTypes]> | |
| type JSONTypes = { | |
| string: string | |
| date: Date | |
| number: number | |
| array: any[] | |
| boolean: boolean | |
| null: null | |
| } | |
| type PickByType<T, Types> = { | |
| [K in keyof T as T[K] extends Types ? K : never]: T[K] | |
| } | |
| export type JSONSchemaReferences = Record<string, unknown> | undefined //Record<string, JSONSchema<undefined>> | undefined | |
| type JSONSchemaValueType<T> = T extends JSONTypes[keyof JSONTypes] | |
| ? keyof PickByType<JSONTypes, T> | |
| : never | |
| type JSONSchemaFromObject<T, References extends JSONSchemaReferences> = { | |
| [K in keyof T]: JSONSchemaValueType<T[K]> | |
| } | |
| function JSONSchema<T>(): JSONSchemaFromObject<T, undefined> { | |
| return undefined! | |
| } | |
| type _JSONSchema<References> = { | |
| [name: string]: | |
| keyof JSONTypes | |
| | keyof References | |
| | `${keyof JSONTypes}[]` | |
| | `${keyof References & string}[]` | |
| //| ProbablyEnum // enums | |
| } | |
| export type JSONSchema<References> = References extends Record<string, _JSONSchema<References>> | |
| ? { | |
| [name: string]: | |
| keyof JSONTypes | |
| | keyof References | |
| | `${keyof JSONTypes}[]` | |
| | `${keyof References & string}[]` | |
| | ProbablyEnum // enums | |
| } | |
| : never | |
| type ProbablyEnum = Record<string, string | number> | |
| type MergedJSONTypes<References extends JSONSchemaReferences> = JSONTypes & { | |
| [K in keyof References as K extends string ? K : never]: | |
| References[K] extends JSONSchema<References> | |
| ? JSONSchemaType<References[K], References> | |
| : never | |
| } | |
| export type JSONSchemaType<T extends JSONSchema<References>, References extends JSONSchemaReferences> = { | |
| -readonly [K in keyof T]: T[K] extends keyof MergedJSONTypes<References> | |
| ? MergedJSONTypes<References>[T[K]] | |
| : T[K] extends `${infer Element & keyof MergedJSONTypes<References>}[]` | |
| ? MergedJSONTypes<References>[Element & keyof MergedJSONTypes<References>][] | |
| : T[K] extends ProbablyEnum | |
| ? `${keyof T[K] & string}` | |
| : T[K] | |
| } | |
| type NestedBetterIntellisense<T> = { | |
| -readonly [K in keyof T]: NestedBetterIntellisense<T[K]> | |
| } | |
| interface JSONValidator { | |
| <T extends JSONSchema<U>, U extends JSONSchemaReferences>( | |
| json: Record<string, unknown>, | |
| schema: T, | |
| references?: U | |
| ): NestedBetterIntellisense<JSONSchemaType<T, U>> | |
| } | |
| export const validator: JSONValidator = null! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment