declare const _mutableMembers: unique symbol; interface MembersTag { [_mutableMembers]: Members; } type PropertyKey = string | symbol; type SimpleValueSpec = null | undefined | boolean | number | PropertyKey | bigint | any[]; interface ComplexValueSpec { value?: T; type?: PropType; required?: boolean; writable?: boolean; depends?: PropertyKey[]; init?(): T; get?(): T; set?(value: T): void; cast?(value: unknown): T; clone?(value: T): T; } type ValueSpec = SimpleValueSpec | ComplexValueSpec; type MethodSpec = (...args: any[]) => any; type SpecialNames = "$inherit" | "$watch" | "$construct"; type WatchAction = (value: T, evt: WatchEvent) => void; type WatchItem = WatchAction | { triggerOnInit?: boolean; depends?: PropertyKey[]; do: WatchAction; }; type MutableDefinition = { $watch?: Record & ThisType>>>>; $construct?(options?: any): void; } & { [P in keyof Exclude]: ValueSpec | MethodSpec | any; }; type IsEmptyObject = T extends {} ? ({} extends T ? true : false) : false; type ResolveSpecType = Spec extends { type: PropType; } ? T : Spec extends { value: infer V; } ? V : Spec extends ComplexValueSpec ? V : never; type NonNullableSpec = { required: true; } | { get(): ResolvedType; } | { cast(v: unknown): ResolvedType; }; type ResolveSpecRequired = Spec extends NonNullableSpec ? NonNullable : ResolvedType | undefined; type InferTypeFromComplexValueSpec = ResolveSpecRequired>; type MapSpecToProperty = Spec extends SimpleValueSpec ? Original | undefined : IsEmptyObject extends true ? Spec | undefined : Spec extends ComplexValueSpec ? InferTypeFromComplexValueSpec : never; type MapSpecToClassMember = Spec extends ValueSpec ? MapSpecToProperty : Spec extends MethodSpec ? Spec : never; type ExcludeSpecialPropertiesAndInvalidSpecs = Exclude extends never ? never : MapSpecToClassMember extends never ? never : Name; type BuildMutableMembers = { [P in keyof Specs as ExcludeSpecialPropertiesAndInvalidSpecs]: MapSpecToClassMember; }; /** Common methods and properties present on all mutable instances. */ interface MutableBase extends MembersTag { /** Call super constructor or method. */ $super(...args: any[]): any; /** * Returns the value associated with the given property name. * * @param property a property name * @returns the current property value */ get>(property: Prop): Properties[Prop]; /** * Accesses an arbitrary property on this instance. * Also supports property paths, e.g. `instance.get("a.b.c")`. * * @param property a property name or property path * @returns the current value */ get(property: string): unknown; /** * Sets the value associated with the given property name. *| undefined * @param property a property name * @param value the new property value */ set>(property: Prop, value: Properties[Prop]): void; /** * Sets an arbitrary property on this instance. * Also supports property paths, e.g. `instance.set("a.b.c", 123)`. * * @param property a property name or property path * @param value the new value */ set(property: string, value: any): void; /** * Watches the given property on this object. * * @param property must be `"*"` or a property name * @param callback will be invoked when the property changes. * @returns a handle to stop watching */ watch>(property: Prop, callback: WatchCallback[Prop]>): WatchHandle; /** * Watches all properties on this object. * * @param property must be `"*"` or a property name * @param callback will be invoked when a property changes. * @returns a handle to stop watching */ watch(property: "*", callback: WatchCallback, Properties[PublicPropertyName]>): WatchHandle; } /** * Event that is emitted when a property value changes. */ interface WatchEvent { /** The name of the property. */ name: Name; /** The old value of the property. */ old: T; /** The current value of the property. */ value: T; } /** Event handler function type for property changes. */ type WatchCallback = (event: WatchEvent) => void; /** Handle that allows disconnecting from a previous `watch()` call. */ interface WatchHandle { /** Stops watching for property changes. */ remove(): void; } type RemoveFunction = T extends (...args: any[]) => any ? never : true; type RemoveFunctions = { [P in keyof T as RemoveFunction extends never ? never : P]: T[P]; }; type PropType = { (): T; } | { new (...args: never[]): T & object; } | { new (...args: string[]): Function; }; /** * This type lists all valid properties of the given mutable, including their types. */ type Properties> = M extends MembersTag ? RemoveFunctions : {}; /** * This type is a union listing all valid property identifiers for the given mutable. */ type PropertyName> = Extract, PropertyKey>; /** Excludes symbol properties from the list of property names (they are not watchable via '*') */ type PublicPropertyName> = Exclude, symbol>; /** * Represents the type of a mutable class created from a definition. * Instances of this type are created by calling the `declare()` function. * * Note that both function call syntax and `new` are supported ways to create a mutable instance. */ interface MutableConstructor { /** Constructs a new instance of the mutable class. */ (options?: Partial>>): Mutable; /** Constructs a new instance of the mutable class. */ new (options?: Partial>>): Mutable; } /** * Represents the type of mutable instances built from some definition. * All properties and methods listed in the original definition are mapped to class members. */ type Mutable = MutableBase & Members; /** * Base class of all Mutable instances. */ declare const Mutable: MutableConstructor; /** * Helper to declare Mutable classes from a specification. This is also the default export. * * NOTE: This overload constructs mutables that use "$inherit". * * @param definition an object with a class specification. * @example * ```ts * const Clazz = declare({ * firstName: "", * lastName: "", * fullName: { * depends: ["firstName", "lastName"], * get(): string { * return `${this.firstName} ${this.lastName}`; * } * } * }); * const x = new Clazz(); * ``` */ declare function declare>(definition: Def & { $inherit: any; } & ThisType & Record>>): MutableConstructor & Record>; /** * Helper to declare Mutable classes from a specification. This is also the default export. * @param definition an object with a class specification. * @example * ```ts * const Clazz = declare({ * firstName: "", * lastName: "", * fullName: { * depends: ["firstName", "lastName"], * get(): string { * return `${this.firstName} ${this.lastName}`; * } * } * }); * const x = new Clazz(); * ``` */ declare function declare>(definition: Def & ThisType>>): MutableConstructor>; /** * Helper to trigger custom change notification. * @param instance the instance which property. * @param name the name of the changed property. * @example * ```ts * import {declare, notifyChange} from "apprt-core/Mutable"; * let globalCounter = 0; * const Clazz = declare({ * counter: { * get() { * return globalCounter; * } * } * }); * * const instance = new Clazz(); * instance.watch("counter", ({ value }) => { * console.log(`count: ${value}`); * }); * * // ensure that external counter can be watched * globalCounter++; * notifyChange(instance, "counter"); * //-> count: 1 * ``` */ declare function notifyChange>(instance: M, name: PropertyName): void; /** * Helper to introspect the public available property names. * @param instance the instance to introspect. * @param skipOwn skip additional object property names that were not part of the original `declare()`. * @returns property names * @example * ```ts * const Clazz = declare({ * firstName: "", * lastName: "" * }); * const instance = new Clazz(); * const names = propertyNames(instance); * //-> names === ["firstName","lastName"] * ``` */ declare function propertyNames(instance: Mutable, skipOwn?: boolean): string[]; /** * Helper to transform given mutable instance into flat object. * Properties with undefined values are not copied. * @param instance the instance to introspect. * @returns values of all properties; * @example * ```ts * const Clazz = declare({ * firstName: "", * lastName: "" * }); * const instance = new Clazz({firstName:"Test"}); * const state = toObject(instance); * //-> { firstName: "Test", lastName:"" } * ``` */ declare function toObject(instance: Mutable): Record; /** * Helper to extend an existing class with new properties. * @param clazzOrObject the clazz to extend. * @param definitions the properties specification. * @example * ``` * class MyClazz extends Mutable { * * } * properties(MyClazz, { * msg: { * value: "test" * } * }); * let o = new MyClazz({ * msg: "test1" * }); * ``` */ declare function properties(clazzOrObject: any, definitions: Record): void; /** * Common constructor function of Mutables. * @param params the named parameters. * @param self the this scope */ declare function construct(params: Record, self: T): T; export { Mutable, construct, declare, declare as default, notifyChange, properties, propertyNames, toObject }; export type { MutableConstructor, MutableDefinition, PropType, Properties, PropertyName, WatchCallback, WatchEvent, WatchHandle };