? unknown
: OwnKey
: never]: Unwrap;
}>;
type ExplicitFields = Partial<{
[P in keyof T as T[P] extends Enforced
? T[P] extends ANY
? OwnKey
: never
: OwnKey
]: T[P];
}>;
type Init = EnforcedFields & ExplicitFields;
/**
* Abstract class that allows defining custom data classes. Should be extended
* with a set of class fields that define the shape of desired model.
*
* ```ts
* import { Data, type Enforced } from "dataclass";
*
* class Project extends Data {
* // this property is required when creating an instance
* id: Enforced;
* // these properties have defaults but can be overwritten
* name: string = "Untitled";
* createdBy: string = "Anon";
* // this property may contain null and won't be required
* createdAt: Date | null = null;
* }
*
* let project = Project.create({
* id: 'abc123',
* createdBy: 'Oleksii',
* });
* // > Project { id: 'abc123', name: 'Untitled', createdBy: 'Oleksii', createdAt: null }
* ```
*
* @link https://dataclass.js.org
*/
export class Data {
/**
* Instantiate the data class. Provide custom values that should override
* defaults. If the class has optional properties, create() method will
* require to explicitly define them.
*
* ```ts
* class User extends Data {
* name: string = "Anon";
* age: number | null = null;
* }
* ```
*
* @link https://dataclass.js.org/guide/getting-started.html
*/
static create(
this: { new (): Type },
...values: EnforcedFields extends Record ? [Init?] : [Init]
): Type;
/**
* Create new immutable instance based on existing one, with some properties changed.
*
* ```ts
* class User extends Data {
* name: string = "Anon";
* age: number | null = null;
* }
*
* let initial = User.create({ name: "Liza" });
*
* // creates an immutable copy with previously defined
* // `name: "Liza"` and additionaly defined `age: 28`
* let updated = initial.copy({ age: 28 });
* ```
*/
copy(values?: Partial>): this;
/**
* Compare the instance to another instance of the same data class.
*
* @link https://dataclass.js.org/guide/objects-equality.html
*/
equals(other: this): boolean;
}
================================================
FILE: typings/dataclass.js.flow
================================================
/* @flow */
/**
* Special generic type to mark data class field as required to be initialized
* at creation time. Can be used along with ! (exclamation mark operator) to
* define a field without a default value, but explicit type and make `create()`
* method require this field to be provided.
*/
declare export opaque type Enforced : T;
declare type OptKeys = $Values<{ [P in $Keys]: T[P] extends Enforced ? P : empty}>;
declare type EnforcedFields = Pick<{ [P in $Keys]: T[P] extends Enforced ? V : T[P] }, OptKeys>;
declare type ExplicitFields = Omit<{ [P in $Keys]: T[P] extends Enforced ? V : T[P] }, OptKeys>;
// do I still need Required for Optional part?
declare type Values = { ...EnforcedFields, ...Partial> };
declare type Init = EnforcedFields extends Record ? Values | void : Values;
declare type Shape = Partial>;
/**
* Abstract class that allows defining custom data classes. Should be extended
* with a set of class fields that define the shape of desired model.
*
* ```js
* // @flow
* import { Data, type Enforced } from "dataclass";
*
* class Project extends Data {
* // this property is required when creating an instance
* id: Enforced;
* // these properties have defaults but can be overwritten
* name: string = "Untitled";
* createdBy: string = "Anon";
* // this property may contain null and won't be required
* createdAt: Date | null = null;
* }
*
* let project = Project.create({
* id: 'abc123',
* createdBy: 'Oleksii',
* });
* // > Project { id: 'abc123', name: 'Untitled', createdBy: 'Oleksii', createdAt: null }
* ```
*
* @link https://dataclass.js.org
*/
declare export class Data {
/**
* Instantiate the data class. Provide custom values that should override
* defaults. If the class has optional properties, create() method will
* require to explicitly define them.
*
* ```js
* // @flow
*
* class User extends Data {
* name: string = "Anon";
* age: number | null = null;
* }
* ```
*
* @link https://dataclass.js.org/guide/getting-started.html
*/
static create(values: Init): this;
/**
* Create new immutable instance based on existing one, with some properties changed.
*
* ```js
* // @flow
*
* class User extends Data {
* name: string = "Anon";
* age: number | null = null;
* }
*
* let initial = User.create({ name: "Liza" });
*
* // creates an immutable copy with previously defined
* // `name: "Liza"` and additionaly defined `age: 28`
* let updated = initial.copy({ age: 28 });
* ```
*/
copy(values?: Shape): this;
/**
* Compare the instance to another instance of the same data class.
*
* @link https://dataclass.js.org/guide/objects-equality.html
*/
equals(other: this): boolean;
}
================================================
FILE: typings/runtime.d.ts
================================================
import { Enforced } from "./dataclass";
export function runtime(C: T, ...args: any): T;
interface TypedField {
(): Enforced;
(defaults: Type): Type;
(defaults?: Type, options: { required: true }): Enforced;
(defaults?: Type, options: { optional: true }): Type | undefined;
(defaults?: Type, options: { nullable: true }): Type | null;
}
interface LiteralField {
(defaults: Type): Type;
}
interface ConstField {
(): Value;
(options: { required: true }): Enforced;
}
interface InstanceField {
(Ctor: { new (...args: any): Class }, defaults?: Class): Class;
}
// how do I make it possible to use null|undefined
interface UnionField {
(fields: T): Enforced<
T extends Array ? (V extends Enforced ? VV : V) : T
>;
(
fields: T,
defaults: T extends Array ? (V extends Enforced ? VV : V) : T,
): T extends Array ? (V extends Enforced ? VV : V) : T;
(
fields: T,
defaults?: T extends Array ? (V extends Enforced ? VV : V) : T,
options: { required: true },
): Enforced ? (V extends Enforced ? VV : V) : T>;
(
fields: T,
defaults?: T extends Array ? (V extends Enforced ? VV : V) : T,
options: { optional: true },
): (T extends Array ? (V extends Enforced ? VV : V) : T) | undefined;
(
fields: T,
defaults?: T extends Array ? (V extends Enforced ? VV : V) : T,
options: { nullable: true },
): (T extends Array ? (V extends Enforced ? VV : V) : T) | null;
}
interface ObjectField {
>(schema: Type): Enforced<{
[P in keyof Type]: Type[P] extends Enforced ? V : Type[P];
}>;
>(
schema: Type,
defaults: NoInfer<{ [P in keyof Type]: Type[P] extends Enforced ? V : Type[P] }>,
): { [P in keyof Type]: Type[P] extends Enforced ? V : Type[P] };
>(
schema: Type,
defaults?: NoInfer<{ [P in keyof Type]: Type[P] extends Enforced ? V : Type[P] }>,
options: { required: true },
): Enforced<{ [P in keyof Type]: Type[P] extends Enforced ? V : Type[P] }>;
>(
schema: Type,
defaults?: NoInfer<{ [P in keyof Type]: Type[P] extends Enforced ? V : Type[P] }>,
options: { optional: true },
): { [P in keyof Type]: Type[P] extends Enforced ? V : Type[P] } | undefined;
>(
schema: Type,
defaults?: NoInfer<{ [P in keyof Type]: Type[P] extends Enforced ? V : Type[P] }>,
options: { nullable: true },
): { [P in keyof Type]: Type[P] extends Enforced ? V : Type[P] } | null;
}
interface ArrayField {
(schema: Type): Enforced<
(Type extends Array ? (V extends Enforced ? VV : V) : Type)[]
>;
(
schema: Type,
defaults: NoInfer<
(Type extends Array ? (V extends Enforced ? VV : V) : Type)[]
>,
): (Type extends Array ? (V extends Enforced ? VV : V) : Type)[];
(
schema: Type,
defaults?: NoInfer<
(Type extends Array ? (V extends Enforced ? VV : V) : Type)[]
>,
options: { required: true },
): Enforced<(Type extends Array ? (V extends Enforced ? VV : V) : Type)[]>;
(
schema: Type,
defaults?: NoInfer<
(Type extends Array ? (V extends Enforced ? VV : V) : Type)[]
>,
options: { optional: true },
): (Type extends Array ? (V extends Enforced ? VV : V) : Type)[] | undefined;
(
schema: Type,
defaults?: NoInfer<
(Type extends Array ? (V extends Enforced ? VV : V) : Type)[]
>,
options: { nullable: true },
): (Type extends Array ? (V extends Enforced ? VV : V) : Type)[] | null;
}
interface DataFields {
string: TypedField;
number: TypedField;
boolean: TypedField;
bigint: TypedField;
symbol: TypedField;
literal: LiteralField;
null: ConstField;
undefined: ConstField;
// can i use return type to get result of applying Class to TypedField?
instance: InstanceField;
regexp: TypedField;
date: TypedField;
union: UnionField;
unknown: ConstField; // should have required option
required: { required: true };
optional: { optional: true };
nullable: { nullable: true };
object: ObjectField;
array: ArrayField;
}
export let data: DataFields;
================================================
FILE: typings/runtime.js.flow
================================================
/* @flow */
declare export function runtime(options?: {}): (C: T) => T;
declare export let data: Record = {};
================================================
FILE: vitest.config.ts
================================================
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
coverage: {
enabled: true,
provider: "istanbul",
},
},
});