@ayka/strukt
Version:
A lightweight TypeScript library that simplifies the creation of structured data objects and classes. It provides a type-safe and flexible way to define classes with custom properties and initialization logic.
500 lines (480 loc) • 20.6 kB
TypeScript
/**
* Represents a class constructor type.
* @template args - An array of types representing the constructor arguments. Defaults to any[].
* @template instance - The type of the instance created by the constructor. Defaults to any.
*/
type klass$1<args extends Array<any> = Array<any>, instance = any> = {
new (...args: args): instance;
};
type anyClass = klass$1<any[], any>;
/**
* Represents a class constructor type with a single input parameter.
* @template input - The type of the single input parameter for the constructor. Defaults to any.
* @template result - The type of the instance created by the constructor. Defaults to any.
*/
type klass1<input = any, result = any> = klass$1<[input], result>;
/**
* Extracts the constructor parameters of a class type.
* @template t - The class constructor type.
*/
type classArgs<t extends klass$1> = ConstructorParameters<t>;
/**
* Extracts the nth constructor parameter of a class type.
* @template t - The class constructor type.
* @template n - The index of the constructor parameter to extract.
*/
type classArgsN<
t extends klass$1,
n extends number,
> = ConstructorParameters<t>[n];
/**
* Extracts the first constructor parameter of a class type.
* @template t - The class constructor type.
*/
type classArgs1<t extends klass$1> = classArgsN<t, 0>;
type anyObject = Record<any, any>;
type anyArgs = any[];
type anyConstructor = (...args: any[]) => anyObject;
type anyConstructor1 = (arg: any) => anyObject;
type fnParam1<fn extends anyConstructor> = Parameters<fn>[0];
type isNever<t> = [t] extends [never] ? true : false;
type types_d_anyArgs = anyArgs;
type types_d_anyClass = anyClass;
type types_d_anyConstructor = anyConstructor;
type types_d_anyConstructor1 = anyConstructor1;
type types_d_anyObject = anyObject;
type types_d_classArgs<t extends klass$1> = classArgs<t>;
type types_d_classArgs1<t extends klass$1> = classArgs1<t>;
type types_d_classArgsN<t extends klass$1, n extends number> = classArgsN<t, n>;
type types_d_fnParam1<fn extends anyConstructor> = fnParam1<fn>;
type types_d_isNever<t> = isNever<t>;
type types_d_klass1<input = any, result = any> = klass1<input, result>;
declare namespace types_d {
export type { types_d_anyArgs as anyArgs, types_d_anyClass as anyClass, types_d_anyConstructor as anyConstructor, types_d_anyConstructor1 as anyConstructor1, types_d_anyObject as anyObject, types_d_classArgs as classArgs, types_d_classArgs1 as classArgs1, types_d_classArgsN as classArgsN, types_d_fnParam1 as fnParam1, types_d_isNever as isNever, klass$1 as klass, types_d_klass1 as klass1 };
}
/**
* Creates a class constructor type with no arguments.
* @template t - The type of the instance created by the constructor.
* @returns {T.klass<[], t>} A class constructor type.
*/
declare const klass: <t>() => klass$1<[], t>;
/**
* Selects specified keys from a target object in a type-safe manner.
*
* @template t - The type of the target object.
* @template key - The type of the keys to select, extending keyof t.
* @param {t} target - The target object to select keys from.
* @param {Iterable<key>} keys - An iterable of keys to select.
* @returns {{ [key in key]: t[key] }} An object with the selected keys and their values.
*
* @example
* const obj = { a: 1, b: 2, c: 3 };
* const result = selectKeys(obj, ['a', 'c']);
* console.log(result); // Output: { a: 1, c: 3 }
*
* @example
* class Person {
* constructor(public name: string, public age: number, private ssn: string) {}
* toObject() {
* return selectKeys(this, ['name', 'age']);
* }
* }
* const person = new Person('John', 30, '123-45-6789');
* const result = person.toObject();
* console.log(result); // Output: { name: 'John', age: 30 }
*/
declare const selectKeys: <t, key extends keyof t = keyof t>(target: t, keys: Exclude<Iterable<key>, string>) => { -readonly [k in key]: t[k]; };
/**
* Creates a function that initializes an instance of a given class.
*
* @template k - The type of the class constructor.
* @param {k} klass - The class constructor to create an initializer for.
* @returns {(...params: ConstructorParameters<k>) => InstanceType<k>} A function that takes constructor parameters and returns a new instance of the class.
*
* @example
* class Person {
* constructor(public name: string, public age: number) {}
* }
*
* const initPerson = makeConstructor(Person);
* const john = initPerson('John', 30);
* console.log(john); // Person { name: 'John', age: 30 }
*/
declare const makeConstructor: <k extends klass$1>(klass: k) => (...params: ConstructorParameters<k>) => InstanceType<k>;
/**
* Redefines specified properties of an object as accessors (getters and setters).
* This function mutates the original object and returns it.
* The redefined properties will behave like normal properties when accessed or modified,
* but they will be implemented as getters and setters internally.
* This is useful for hiding properties from console.log, as they will be printed as [Getter/Setter].
*
* @template t - The type of the target object.
* @template key - The type of the keys to be redefined, must be a subset of keyof t.
* @param {t} target - The object whose properties are to be redefined.
* @param {Iterable<key>} props - An iterable of property names to be redefined as accessors.
* @returns {t} The modified target object.
*
* @example
* const obj = { a: 1, b: 2, c: 3 };
* redefineAsAccessors(obj, ['a', 'b']);
* console.log(obj); // Outputs: { a: [Getter/Setter], b: [Getter/Setter], c: 3 }
* obj.a = 4;
* console.log(obj.a); // Outputs: 4
* console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // Shows getter and setter
*/
declare const redefineAsAccessors: <t, key extends keyof t>(target: t, props: Iterable<key>) => t;
/**
* Options for the lazy decorator.
* @property {boolean} - Whether to use a value property instead of a getter/setter.
* @property {boolean} - Whether the property is configurable.
* @property {boolean} - Whether the property is enumerable.
* @property {boolean} - Whether the property is writable.
*/
type lazyOpts = {
useValue?: boolean;
configurable?: boolean;
enumerable?: boolean;
writable?: boolean;
};
/**
* A decorator that lazily initializes a property.
* @param {lazyOpts} [opts] - Options for the lazy initialization.
* @returns {PropertyDecorator} A property decorator function.
*
* @example
* class Example {
* @lazy()
* get expensiveValue() {
* console.log('Calculating...');
* return 42;
* }
* }
*
* const instance = new Example();
* console.log(instance.expensiveValue); // Logs "Calculating..." then "42"
* console.log(instance.expensiveValue); // Logs "42" without recalculating
*/
declare const lazy: (opts?: lazyOpts) => (_target: any, propertyKey: PropertyKey, descriptor: PropertyDescriptor) => void;
/**
* Converts an object with promise values into a promise of an object with resolved values.
* This is useful for handling multiple promises with named results.
*
* @template shape - An object type where values are Promises
* @param {shape} promiseShape - An object containing promises as values
* @returns {Promise<{ [k in keyof shape]: Awaited<shape[k]> }>} A promise that resolves to an object with the same keys but resolved values
*
* @example
* const result = await promiseObject({
* user: fetchUser(123),
* posts: fetchUserPosts(123),
* settings: fetchSettings()
* });
* // result is typed as:
* // {
* // user: User,
* // posts: Post[],
* // settings: Settings
* // }
* console.log(result.user, result.posts, result.settings);
*/
declare const promiseObject: <shape extends {
[key: string]: Promise<any>;
}>(promiseShape: shape) => Promise<{ [k in keyof shape]: Awaited<shape[k]>; }>;
declare const lib_klass: typeof klass;
declare const lib_lazy: typeof lazy;
type lib_lazyOpts = lazyOpts;
declare const lib_makeConstructor: typeof makeConstructor;
declare const lib_promiseObject: typeof promiseObject;
declare const lib_redefineAsAccessors: typeof redefineAsAccessors;
declare const lib_selectKeys: typeof selectKeys;
declare namespace lib {
export { lib_klass as klass, lib_lazy as lazy, type lib_lazyOpts as lazyOpts, lib_makeConstructor as makeConstructor, lib_promiseObject as promiseObject, lib_redefineAsAccessors as redefineAsAccessors, lib_selectKeys as selectKeys };
}
type params$2<data extends anyObject> = {
data: data;
hidden: readonly PropertyKey[];
};
type constructParams = {
target: anyObject;
data: anyObject;
hidden: readonly PropertyKey[];
};
/**
* Constructs an object by copying data and redefining hidden properties as accessors.
* @param params - The parameters for construction.
* @returns The constructed target object.
*/
declare const construct: (params: constructParams) => anyObject;
declare class BasicStrukt {
constructor(params: params$2<any>);
}
declare class ExtendedStrukt<args extends anyArgs, data extends anyObject> extends BasicStrukt {
#private;
constructor(params: params$2<data>);
/**
* Gets the type of arguments.
* @returns The type of arguments.
*/
get $$argsType(): args;
/**
* Gets the first argument type.
* @returns The first argument type.
*/
get $$args1Type(): args[0];
/**
* Gets the type of data returned by the constructor.
* @returns The type of data.
*/
get $$dataType(): data;
/**
* Retrieves the keys of the data returned by the constructor.
* @returns An array of data keys.
*/
$dataKeys(): (keyof data)[];
/**
* Creates a data object from the instance.
* @returns The data object.
*/
$data(): data;
/**
* Selects specific keys from the object.
* @param keys - The keys to select.
* @returns An object with the selected keys.
*/
$selectKeys<keys extends keyof this>(keys: keys[]): { -readonly [k in keys]: this[k]; };
/**
* Creates a clone of the current object.
* @returns A cloned instance of the object.
*/
$clone(): this;
/**
* Updates the object with a patch.
* @param patch - The patch to apply.
* @returns A new instance with the applied patch.
*/
$update(patch: Partial<typeof this.$$dataType>): this;
/**
* Applies a patch function to the object.
* @param fn - The function that returns a patch.
* @returns A new instance with the applied patch.
*/
$patch(fn: (data: this) => Partial<typeof this.$$dataType>): this;
/**
* Creates a new instance of the object.
* @param args - The arguments to pass to the constructor.
* @returns A new instance of the object.
*/
$create(...args: args): this;
}
type StruktBase_BasicStrukt = BasicStrukt;
declare const StruktBase_BasicStrukt: typeof BasicStrukt;
type StruktBase_ExtendedStrukt<args extends anyArgs, data extends anyObject> = ExtendedStrukt<args, data>;
declare const StruktBase_ExtendedStrukt: typeof ExtendedStrukt;
declare const StruktBase_construct: typeof construct;
type StruktBase_constructParams = constructParams;
declare namespace StruktBase {
export { StruktBase_BasicStrukt as BasicStrukt, StruktBase_ExtendedStrukt as ExtendedStrukt, StruktBase_construct as construct, type StruktBase_constructParams as constructParams, type params$2 as params };
}
/**
* Parameters for initializing a Strukt.
* @template constructor - The constructor type.
* @property {constructor} constructor - The constructor function.
* @property {(keyof ReturnType<constructor>)[]} [hidden] - Optional keys to be hidden.
*/
type params$1<constructor extends anyConstructor> = {
constructor: constructor;
hidden?: (keyof ReturnType<constructor>)[];
};
type basicStruktClass<constructor extends anyConstructor> = {
new (...params: Parameters<constructor>): BasicStrukt & ReturnType<constructor>;
};
type struktClass<constructor extends anyConstructor> = {
new (...params: Parameters<constructor>): ExtendedStrukt<Parameters<constructor>, ReturnType<constructor>> & ReturnType<constructor>;
};
/**
* Initializes a Strukt class.
* @template constructor - The constructor type.
* @param {params<constructor>} params - The parameters for initialization.
* @returns {struktClass<constructor>} The initialized Strukt class.
* @example
* class MyClass extends init({
* constructor (args: { x: number, y: number }) {
* return {
* x: args.x,
* y: args.y,
* sum: args.x + args.y
* };
* },
* hidden: ['sum']
* }) {}
* const instance = new MyClass({ x: 1, y: 2 });
* console.log(instance); // Output: MyClass { x: 1, y: 2, sum: 3 }
*/
declare const init$1: <constructor extends anyConstructor>(params: params$1<constructor>) => struktClass<constructor>;
/**
* Initializes a basic Strukt class with minimal functionality.
* Unlike `init()` which creates a full-featured Strukt, this creates a lightweight version
* that only includes the core data wrapping functionality without utility methods.
* Use this when you only need the basic data wrapping and hidden properties functionality.
* @template constructor - The constructor type.
* @param {params<constructor>} params - The parameters for initialization.
* @returns {basicStruktClass<constructor>} The initialized basic Strukt class with minimal features.
* @example
* class MyBasicClass extends initBasic({
* constructor (args: { x: number, y: number }) {
* return {
* x: args.x,
* y: args.y,
* sum: args.x + args.y
* };
* },
* hidden: ['sum']
* }) {}
* const instance = new MyBasicClass({ x: 1, y: 2 });
* console.log(instance); // Output: MyBasicClass { x: 1, y: 2, sum: 3 }
*/
declare const initBasic: <constructor extends anyConstructor>(params: params$1<constructor>) => basicStruktClass<constructor>;
/**
* Type guard to check if a value is an ExtendedStrukt instance.
* @param {unknown} value - The value to check.
* @returns {boolean} True if the value is an ExtendedStrukt instance.
* @example
* if (isStrukt(someValue)) {
* // someValue is typed as ExtendedStrukt here
* console.log(someValue.$data());
* }
*/
declare const isStrukt: (value: unknown) => value is ExtendedStrukt<any, any>;
/**
* Type guard to check if a value is a BasicStrukt instance.
* @param {unknown} value - The value to check.
* @returns {boolean} True if the value is a BasicStrukt instance.
* @example
* if (isBasicStrukt(someValue)) {
* // someValue is typed as BasicStrukt here
* console.log(someValue);
* }
*/
declare const isBasicStrukt: (value: unknown) => value is BasicStrukt;
type Strukt_basicStruktClass<constructor extends anyConstructor> = basicStruktClass<constructor>;
declare const Strukt_initBasic: typeof initBasic;
declare const Strukt_isBasicStrukt: typeof isBasicStrukt;
declare const Strukt_isStrukt: typeof isStrukt;
type Strukt_struktClass<constructor extends anyConstructor> = struktClass<constructor>;
declare namespace Strukt {
export { type Strukt_basicStruktClass as basicStruktClass, init$1 as init, Strukt_initBasic as initBasic, Strukt_isBasicStrukt as isBasicStrukt, Strukt_isStrukt as isStrukt, type params$1 as params, type Strukt_struktClass as struktClass };
}
/**
* Represents metadata for an error, including optional message and cause.
*/
type errorMeta = Record<string, any> & {
message?: string;
cause?: any;
};
/**
* Base class for structured errors with metadata.
*/
declare class ErrorStruktBase extends Error {
message: string;
readonly meta: errorMeta;
constructor(msg: string, metaInput?: errorMeta);
}
type staticErrorInstance = ErrorStruktBase;
type staticErrorClass = {
new (meta?: errorMeta): staticErrorInstance;
new (message: string, meta?: errorMeta): staticErrorInstance;
};
/**
* Parameters for creating a static error.
*/
type staticParams = {
message?: string;
};
/**
* Creates a static error class with optional parameters.
* @param params - Optional parameters for the static error.
* @returns A class for creating static error instances.
* @example
* // Create a static error class with a default message
* class MyError extends staticError({ message: 'Default error message' }) {}
*
* // Create an instance of the error with a specific message
* const errorInstance = new MyError('Specific error message');
* console.log(errorInstance.message); // Output: 'Specific error message'
*
* // Create an instance with metadata
* const meta = { annotation: 'test' };
* const errorWithMeta = new MyError(meta);
* console.log(errorWithMeta.meta); // Output: { annotation: 'test' }
*/
declare const staticError: (params?: staticParams) => staticErrorClass;
/**
* Represents an instance of an error with data.
*/
type errorInstance<t> = staticErrorInstance & {
data: t;
};
type errorClass<constructor extends anyErrConstructor> = fnParam1<constructor> extends Exclude<fnParam1<constructor>, undefined> ? {
new (args: fnParam1<constructor>, meta?: errorMeta): errorInstance<constructorData<constructor>>;
} : {
new (args?: fnParam1<constructor>, meta?: errorMeta): errorInstance<constructorData<constructor>>;
};
/**
* Extracts the data type from a constructor function.
*/
type constructorData<fn extends anyErrConstructor> = ReturnType<fn>['data'];
/**
* Represents a constructor function for errors.
*/
type errConstructor<input, data> = (input: input) => {
data?: data;
message?: string;
};
type anyErrConstructor = errConstructor<any, any>;
/**
* Parameters for initializing an error class.
*/
type params<constructor extends anyErrConstructor> = {
constructor: constructor;
};
/**
* Initializes an error class with a given constructor.
* @param params - Parameters including the constructor function.
* @returns A class for creating error instances.
* @example
* // Define a constructor function for the error
* class MyError extends init({
* constructor(input: { value: number }) {
* return {
* data: { value: input.value, isEven: input.value % 2 === 0 },
* message: `Error with value ${input.value}`,
* };
* }
* }) {}
*
* // Create an instance of the error
* const errorInstance = new MyError({ value: 42 });
* console.log(errorInstance.message); // Output: 'Error with value 42'
* console.log(errorInstance.data); // Output: { value: 42, isEven: true }
*/
declare const init: <constructor extends anyErrConstructor>(params: params<constructor>) => errorClass<constructor>;
declare const isErrorStrukt: (value: unknown) => value is ErrorStruktBase;
type Error$1_ErrorStruktBase = ErrorStruktBase;
declare const Error$1_ErrorStruktBase: typeof ErrorStruktBase;
type Error$1_anyErrConstructor = anyErrConstructor;
type Error$1_constructorData<fn extends anyErrConstructor> = constructorData<fn>;
type Error$1_errConstructor<input, data> = errConstructor<input, data>;
type Error$1_errorClass<constructor extends anyErrConstructor> = errorClass<constructor>;
type Error$1_errorInstance<t> = errorInstance<t>;
type Error$1_errorMeta = errorMeta;
declare const Error$1_init: typeof init;
declare const Error$1_isErrorStrukt: typeof isErrorStrukt;
type Error$1_params<constructor extends anyErrConstructor> = params<constructor>;
declare const Error$1_staticError: typeof staticError;
type Error$1_staticErrorClass = staticErrorClass;
type Error$1_staticErrorInstance = staticErrorInstance;
type Error$1_staticParams = staticParams;
declare namespace Error$1 {
export { Error$1_ErrorStruktBase as ErrorStruktBase, type Error$1_anyErrConstructor as anyErrConstructor, type Error$1_constructorData as constructorData, type Error$1_errConstructor as errConstructor, type Error$1_errorClass as errorClass, type Error$1_errorInstance as errorInstance, type Error$1_errorMeta as errorMeta, Error$1_init as init, Error$1_isErrorStrukt as isErrorStrukt, type Error$1_params as params, Error$1_staticError as staticError, type Error$1_staticErrorClass as staticErrorClass, type Error$1_staticErrorInstance as staticErrorInstance, type Error$1_staticParams as staticParams };
}
export { BasicStrukt as Basic, Error$1 as Error, ExtendedStrukt as Extended, lib as Lib, Strukt, StruktBase, types_d as Types, type classArgs, type classArgs1, type classArgsN, init as error, type params as errorParams, init$1 as init, initBasic, isBasicStrukt, isErrorStrukt, isStrukt, klass, lazy, type lazyOpts, makeConstructor, type params$1 as params, promiseObject, redefineAsAccessors, selectKeys, staticError };