UNPKG

mobx-keystone-mindreframer

Version:

A MobX powered state management solution based on data trees with first class support for Typescript, snapshots, patches and much more

185 lines (160 loc) 4.77 kB
import type { O } from "ts-toolbelt" import type { LateTypeChecker, TypeChecker } from "../typeChecking/TypeChecker" import type { IsOptionalValue } from "../utils/types" /** * @ignore */ export const noDefaultValue = Symbol("noDefaultValue") /** * A model property. */ export interface ModelProp< TPropValue, TPropCreationValue, TIsOptional, TIsId extends boolean = false, THasSetter = never > { $valueType: TPropValue $creationValueType: TPropCreationValue $isOptional: TIsOptional $isId: TIsId $hasSetter: THasSetter defaultFn: (() => TPropValue) | typeof noDefaultValue defaultValue: TPropValue | typeof noDefaultValue typeChecker: TypeChecker | LateTypeChecker | undefined setter: boolean | "assign" withSetter(mode?: boolean | "assign"): ModelPropWithSetter<this> } /** * Any model property. */ export type AnyModelProp = ModelProp<any, any, any, any, any> /** * Model properties. */ export interface ModelProps { [k: string]: AnyModelProp } export type OptionalModelProps<MP extends ModelProps> = { [K in keyof MP]: MP[K]["$isOptional"] & K }[keyof MP] export type ModelPropsToData<MP extends ModelProps> = { [k in keyof MP]: MP[k]["$valueType"] } // we don't use O.Optional anymore since it generates unions too heavy // also if we use pick over the optional props we will loose the ability // to infer generics export type ModelPropsToCreationData<MP extends ModelProps> = { [k in keyof MP]?: MP[k]["$creationValueType"] } & O.Omit< { [k in keyof MP]: MP[k]["$creationValueType"] }, OptionalModelProps<MP> > export type ModelPropsToSetter<MP extends ModelProps> = { [k in keyof MP as MP[k]["$hasSetter"] & `set${Capitalize<k & string>}`]: ( value: MP[k]["$valueType"] ) => void } /** * A property that will be used as model id, replacing $modelId. * Can only be used in models and there can be only one per model. */ export const idProp = (Symbol("idProp") as any) as ModelProp<string, string, string, true> /** * @ignore */ export type OnlyPrimitives<T> = Exclude<T, object> /** * A model prop that maybe / maybe not is optional, depending on if the value can take undefined. */ export type MaybeOptionalModelProp<TPropValue> = ModelProp< TPropValue, TPropValue, IsOptionalValue<TPropValue, string, never> > /** * A model prop that is definitely optional. */ export type OptionalModelProp<TPropValue> = ModelProp< TPropValue, TPropValue | null | undefined, string > /** * A model prop with a generated setter. */ export type ModelPropWithSetter<MP extends AnyModelProp> = Omit<MP, "$hasSetter"> & { $hasSetter: string } /** * Defines a model property, with an optional function to generate a default value * if the input snapshot / model creation data is `null` or `undefined`. * * Example: * ```ts * x: prop(() => 10) // an optional number, with a default value of 10 * x: prop<number[]>(() => []) // an optional number array, with a default empty array * ``` * * @typeparam TValue Value type. * @param defaultFn Default value generator function. * @returns */ export function prop<TValue>(defaultFn: () => TValue): OptionalModelProp<TValue> /** * Defines a model property, with an optional default value * if the input snapshot / model creation data is `null` or `undefined`. * You should only use this with primitive values and never with object values * (array, model, object, etc). * * Example: * ```ts * x: prop(10) // an optional number, with a default value of 10 * ``` * * @typeparam TValue Value type. * @param defaultValue Default primitive value. * @returns */ export function prop<TValue>(defaultValue: OnlyPrimitives<TValue>): OptionalModelProp<TValue> /** * Defines a model property with no default value. * * Example: * ```ts * x: prop<number>() // a required number * x: prop<number | undefined>() // an optional number, which defaults to undefined * ``` * * @typeparam TValue Value type. * @returns */ export function prop<TValue>(): MaybeOptionalModelProp<TValue> // base export function prop<TValue>(def?: any): ModelProp<TValue, any, any, any, any> { let hasDefaultValue = false // default if (arguments.length >= 1) { hasDefaultValue = true } const isDefFn = typeof def === "function" const obj: ReturnType<typeof prop> = { $valueType: null as any, $creationValueType: null as any, $isOptional: null as any, $isId: null as never, $hasSetter: null as never, defaultFn: hasDefaultValue && isDefFn ? def : noDefaultValue, defaultValue: hasDefaultValue && !isDefFn ? def : noDefaultValue, typeChecker: undefined, setter: false, withSetter(mode) { return { ...obj, setter: mode ?? true } }, } return obj as any }