import { Field, Opt, FieldResult, Doc } from "./Doc"; import { List } from "./List"; import { RefField } from "./RefField"; import { DateField } from "./DateField"; import { ScriptField } from "./ScriptField"; import { URLField, WebField, ImageField } from "./URLField"; export type ToType = T extends "string" ? string : T extends "number" ? number : T extends "boolean" ? boolean : T extends ListSpec ? List : // T extends { new(...args: any[]): infer R } ? (R | Promise) : never; T extends DefaultFieldConstructor ? never : T extends { new(...args: any[]): List } ? never : T extends { new(...args: any[]): infer R } ? R : T extends (doc?: Doc) => infer R ? R : never; export type ToConstructor = T extends string ? "string" : T extends number ? "number" : T extends boolean ? "boolean" : T extends List ? ListSpec : new (...args: any[]) => T; export type ToInterface = { [P in Exclude]: T[P] extends DefaultFieldConstructor ? Exclude, undefined> : FieldResult>; }; // type ListSpec = { List: ToContructor> | ListSpec> }; export type ListSpec = { List: ToConstructor }; export type DefaultFieldConstructor = { type: ToConstructor, defaultVal: T }; // type ListType = { 0: List>>, 1: ToType> }[HasTail extends true ? 0 : 1]; export type Head = T extends [any, ...any[]] ? T[0] : never; export type Tail = ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) ? TT : []; export type HasTail = T extends ([] | [any]) ? false : true; export type InterfaceValue = ToConstructor | ListSpec | DefaultFieldConstructor | ((doc?: Doc) => any); //TODO Allow you to optionally specify default values for schemas, which should then make that field not be partial export interface Interface { [key: string]: InterfaceValue; // [key: string]: ToConstructor | ListSpec; } export type WithoutRefField = T extends RefField ? never : T; export type CastCtor = ToConstructor | ListSpec; export function Cast(field: FieldResult, ctor: T): FieldResult>; export function Cast(field: FieldResult, ctor: T, defaultVal: WithoutList>> | null): WithoutList>; export function Cast(field: FieldResult, ctor: T, defaultVal?: ToType | null): FieldResult> | undefined { if (field instanceof Promise) { return defaultVal === undefined ? field.then(f => Cast(f, ctor) as any) as any : defaultVal === null ? undefined : defaultVal; } if (field !== undefined && !(field instanceof Promise)) { if (typeof ctor === "string") { if (typeof field === ctor) { return field as ToType; } } else if (typeof ctor === "object") { if (field instanceof List) { return field as any; } } else if (field instanceof (ctor as any)) { return field as ToType; } } return defaultVal === null ? undefined : defaultVal; } export function NumCast(field: FieldResult, defaultVal: number | null = 0) { return Cast(field, "number", defaultVal); } export function StrCast(field: FieldResult, defaultVal: string | null = "") { return Cast(field, "string", defaultVal); } export function BoolCast(field: FieldResult, defaultVal: boolean | null = false) { return Cast(field, "boolean", defaultVal); } export function DateCast(field: FieldResult) { return Cast(field, DateField, null); } export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) { return Cast(field, ScriptField, defaultVal); } export function WebCast(field: FieldResult, defaultVal: WebField | null = null) { return Cast(field, WebField, defaultVal); } export function ImageCast(field: FieldResult, defaultVal: ImageField | null = null) { return Cast(field, ImageField, defaultVal); } type WithoutList = T extends List ? (R extends RefField ? (R | Promise)[] : R[]) : T; export function FieldValue>(field: FieldResult, defaultValue: U): WithoutList; export function FieldValue(field: FieldResult): Opt; export function FieldValue(field: FieldResult, defaultValue?: T): Opt { return (field instanceof Promise || field === undefined) ? defaultValue : field; } export interface PromiseLike { then(callback: (field: Opt) => void): void; } export function PromiseValue(field: FieldResult): PromiseLike> { return field instanceof Promise ? field : { then(cb: ((field: Opt) => void)) { return cb(field); } }; }