mobx-bonsai
Version:
A fast lightweight alternative to MobX-State-Tree + Y.js two-way binding
230 lines (229 loc) • 10.3 kB
TypeScript
import { IComputedValueOptions } from 'mobx';
import { Merge, SetOptional } from 'type-fest';
import { Dispose } from '../../utils/disposable';
import { PrependArgument } from '../../utils/PrependArgument';
import { NodeTypeKey, NodeTypeValue, nodeTypeKey } from './nodeType';
/**
* Base interface for node types with type-specific capabilities.
*
* @template TNode - The object type representing a node
* @template TCapabilities - Node type capabilities, can be "typed" and/or "keyed"
* @template TOptional - Optional properties of the node type
* @template TKey - Property key used for node identification (if keyed)
* @template TOther - Additional type information
*/
interface BaseNodeTypeWithType<TNode extends object, TCapabilities extends "typed" | "keyed", TOptional extends keyof TNode, TKey extends keyof TNode | never, TOther> {
/**
* Unique identifier for this node type
*/
typeId: TNode extends {
[nodeTypeKey]: NodeTypeValue;
} ? TNode[NodeTypeKey] : undefined;
/**
* Checks if the node is of a specific type
*
* @param node - Node to check
* @returns true if the node type matches, false otherwise
*/
nodeIsOfType(node: object): node is TNode;
/**
* Unregisters this node type
*/
unregister(): void;
/**
* Unregisters this node type (disposable pattern)
*/
[Symbol.dispose](): void;
/**
* Registers a callback to run when nodes of this type are initialized
*
* @param callback - Function to execute when a node is initialized
*
* @returns The same node type with the added initialization callback
*/
onInit(callback: (node: TNode) => void): BaseNodeType<TNode, TCapabilities, TOptional, TKey, TOther>;
_initNode(node: TNode): void;
_addOnInit(callback: (node: TNode) => void): Dispose;
/**
* Configures this type to use a specific property as the node key
*
* @template TKey - Property key in the node type
* @param key - Property name to use as the node key
* @returns A keyed node type using the specified property as key
*/
withKey<TKey extends keyof TNode>(key: TKey): BaseNodeType<TNode, TCapabilities | "keyed", TOptional, TKey, TOther>;
/**
* Makes this node type immutable.
*
* Immutable nodes cannot be modified after creation and sub-objects are not turned into nodes.
* This is useful for creating read-only nodes or for performance optimizations.
*/
frozen(): BaseNodeType<TNode, TCapabilities, TOptional, TKey, TOther>;
/**
* true if the node type is frozen, false otherwise
*/
isFrozen: boolean;
}
/**
* Interface that represents a node type with a key property.
* Provides functionality for accessing and finding nodes by their unique keys.
*
* @template TNode - The node type
* @template TKey - The key of the node's unique identifier property
*/
interface BaseNodeTypeWithKey<TNode, TKey extends keyof TNode> {
/**
* Property name containing the node's unique key
*/
key: TKey;
/**
* Gets the unique key value for a node
*
* @param node - Node to get the key from
* @returns The node's key value or undefined
*/
getKey(node: TNode): TNode[TKey] | undefined;
/**
* Retrieves a node by its key (if it exists)
*
* @param key - Key to search for
* @returns The node with the specified key or undefined
*/
findByKey(key: TNode[TKey]): TNode | undefined;
}
/**
* Base node type definition with core functionality
*
* @template TNode - Node structure that adheres to this type
* @template TOptional - Optional keys in the node structure
* @template TCapabilities - Type of capabilities (untyped, typed or keyed)
* @template TKey - Key field in the node structure (if any)
* @template TOther - Additional properties and methods
*/
export type BaseNodeType<TNode extends object, TCapabilities extends "untyped" | "typed" | "keyed", TOptional extends keyof TNode, TKey extends keyof TNode | never, TOther> = {
/**
* Node constructor.
* Requires all keys from TNode except those in TOptional (which may be omitted).
*/
(data: SetOptional<TNode, TOptional | TKey>): TNode;
/**
* Returns a snapshot based on the provided data.
*/
snapshot(data: SetOptional<TNode, TOptional | TKey>): TNode;
/**
* Adds volatile state properties to nodes of this type
*
* Volatile state is not persisted in snapshots and is local to each node instance.
*
* @template TVolatiles - Record of volatile property getter functions
* @param volatile - Object where each key defines a getter function for volatile state
* @returns The same NodeType with added accessor methods for the volatile state
*/
volatile<TVolatiles extends Record<string, () => any>>(volatile: TVolatiles): BaseNodeType<TNode, TCapabilities, TOptional, TKey, Merge<TOther, VolatileAccessors<TVolatiles, TNode>>>;
/**
* Registers action methods for nodes of this type
*
* Actions are methods that can modify the node state and are automatically
* wrapped in MobX actions for proper state tracking.
*
* @template TActions - Record of action methods
*
* @param actions - An object of action methods
*
* @returns The same NodeType with added action methods that accept a node as their first parameter
*/
actions<TActions extends Record<string, (this: TNode, ...args: any[]) => any>>(actions: TActions): BaseNodeType<TNode, TCapabilities, TOptional, TKey, Merge<TOther, {
[k in keyof TActions]: PrependArgument<TActions[k], TNode>;
}>>;
/**
* Registers getter methods for nodes of this type
*
* Getters are methods that derive values from the node state without modifying it.
*
* @template TGetters - Record of getter methods
*
* @param getters - An object of getter methods
*
* @returns The same NodeType with added getter methods that accept a node as their first parameter
*/
getters<TGetters extends Record<string, (this: TNode, ...args: any[]) => any>>(getters: TGetters): BaseNodeType<TNode, TCapabilities, TOptional, TKey, Merge<TOther, {
[k in keyof TGetters]: PrependArgument<TGetters[k], TNode>;
}>>;
/**
* Registers computed methods for nodes of this type
*
* Computed methods derive values from the node state and are automatically
* memoized by MobX for performance optimization.
*
* @template TComputeds - Record of computed properties
* @param computeds - Function that receives a node and returns an object of computed accessor methods
* @returns The same NodeType with added computed methods that accept a node as their first parameter
*/
computeds<TComputeds extends Record<string, ComputedEntry<TNode, any>>>(computeds: TComputeds): BaseNodeType<TNode, TCapabilities, TOptional, TKey, Merge<TOther, {
[k in keyof TComputeds]: TComputeds[k] extends () => any ? PrependArgument<TComputeds[k], TNode> : TComputeds[k] extends ComputedFnWithOptions<TNode, any> ? PrependArgument<TComputeds[k]["get"], TNode> : never;
}>>;
/**
* Generates setter methods for specified properties
*
* @param properties - Names of properties to create setters for
* @returns The same NodeType with added setter methods
*/
settersFor<K extends keyof TNode & string>(...properties: readonly K[]): BaseNodeType<TNode, TCapabilities, TOptional, TKey, Merge<TOther, {
[P in K as `set${Capitalize<P>}`]: (node: TNode, value: Readonly<TNode[P]>) => void;
}>>;
/**
* Define default values for keys in TOptional.
* When omitted, those properties are filled with the results of these generators.
*
* @template TGen - Record of default value generators
*/
defaults<TGen extends {
[K in keyof TNode]?: () => TNode[K];
}>(defaultGenerators: TGen): BaseNodeType<TNode, TCapabilities, TOptional | (keyof TGen & keyof TNode), TKey, TOther>;
/**
* Default generators defined so far.
*/
defaultGenerators?: {
[K in keyof TNode]?: () => TNode[K];
};
/**
* Extend this type from another untyped node type.
* The base node type can be a subset of the current node type.
*
* @template TBaseNode - Base node type (must be a subset of TNode)
* @template TExtendedOther - Additional properties and methods from the base type
* @param nodeType - Node type to extend from
*/
extends<TExtendedNode extends object, TExtendedOptional extends keyof TExtendedNode, TExtendedOther>(nodeType: BaseNodeType<TExtendedNode, "untyped", TExtendedOptional, never, TExtendedOther>): TNode extends TExtendedNode ? BaseNodeType<TNode, TCapabilities, TOptional | TExtendedOptional, TKey, Merge<TOther, TExtendedOther>> : never;
_extendsKeys: Set<string>;
} & (TCapabilities extends "typed" | "keyed" ? BaseNodeTypeWithType<TNode, TCapabilities, TOptional, TKey, TOther> & (TCapabilities extends "keyed" ? BaseNodeTypeWithKey<TNode, TKey> : unknown) : unknown) & TOther;
/**
* Configuration for a computed property with options
*
* @template TThis - This type for the computed function
* @template T - Return type of the computed value
*/
export type ComputedFnWithOptions<TThis, T> = {
get: (this: TThis) => T;
} & Omit<IComputedValueOptions<T>, "get" | "set">;
/**
* Computed property definition that can be a function or configuration object
*
* @template TThis - This type for the computed function
* @template T - Return type of the computed value
*/
export type ComputedEntry<TThis, T> = ((this: TThis) => T) | ComputedFnWithOptions<TThis, T>;
/**
* Generates accessor methods for volatile properties
*
* @template T - Record of volatile property getter functions
* @template TNode - The node type these accessors operate on
*/
export type VolatileAccessors<T extends Record<string, () => any>, TNode> = {
[K in keyof T as `set${Capitalize<string & K>}`]: (n: TNode, value: ReturnType<T[K]>) => void;
} & {
[K in keyof T as `get${Capitalize<string & K>}`]: (n: TNode) => ReturnType<T[K]>;
} & {
[K in keyof T as `reset${Capitalize<string & K>}`]: (n: TNode) => void;
};
export {};