@arktype/util
Advanced tools
Comparing version 0.0.13 to 0.0.14
import { attest } from "@arktype/attest" | ||
import { Trait, compose } from "../trait2.js" | ||
import { Trait, compose } from "@arktype/util" | ||
@@ -134,3 +134,2 @@ export class Describable extends Trait<{ writeDefaultDescription(): string }> { | ||
class B extends Trait<{ b(): number }> {} | ||
// @ts-expect-error | ||
@@ -137,0 +136,0 @@ attest(class C extends compose(A, B)({}) {}).type.errors( |
@@ -6,2 +6,3 @@ export type pathToString<segments extends string[], delimiter extends string = "/"> = segments extends [] ? "/" : join<segments, delimiter>; | ||
export declare const intersectUniqueLists: <item>(l: readonly item[], r: readonly item[]) => item[]; | ||
export type filter<t extends readonly unknown[], constraint, result extends unknown[] = []> = t extends readonly [infer head, ...infer tail] ? filter<tail, constraint, head extends constraint ? [...result, head] : result> : result; | ||
export type List<t = unknown> = readonly t[]; | ||
@@ -8,0 +9,0 @@ export type listable<t> = t | readonly t[]; |
@@ -68,3 +68,3 @@ import type { Fn } from "./functions.js"; | ||
} | ||
declare const NoopBase: new <t extends object>() => t; | ||
export declare const NoopBase: new <t extends object>() => t; | ||
/** @ts-expect-error **/ | ||
@@ -71,0 +71,0 @@ export declare class CastableBase<t extends object> extends NoopBase<t> { |
@@ -13,3 +13,3 @@ export const entriesOf = (o) => Object.entries(o); | ||
} | ||
const NoopBase = class { | ||
export const NoopBase = class { | ||
}; | ||
@@ -16,0 +16,0 @@ /** @ts-expect-error **/ |
@@ -0,23 +1,54 @@ | ||
import type { conform, evaluate } from "./generics.js"; | ||
import type { intersectParameters } from "./intersections.js"; | ||
import { type Constructor } from "./objectKinds.js"; | ||
import { ShallowClone } from "./records.js"; | ||
export type TraitComposition = <traits extends readonly Constructor[]>(...traits: traits) => compose<traits>; | ||
import { NoopBase, type optionalizeKeys } from "./records.js"; | ||
export type TraitComposition = <traits extends readonly TraitConstructor[]>(...traits: traits) => TraitImplementation<traits, compose<traits>>; | ||
type TraitImplementation<traits extends readonly TraitConstructor[], composed extends ComposedTraits> = <implementation>(implementation: conform<implementation, baseImplementationOf<composed>> & ThisType<implementation & composed["implemented"]>, ...disambiguation: baseDisambiguationOf<traits, implementation, composed> extends infer disambiguation ? {} extends disambiguation ? [] : [disambiguation] : never) => TraitsBase<composed, implementation>; | ||
type TraitsBase<composed extends ComposedTraits, implementation> = composed["statics"] & (new (...args: composed["params"]) => evaluate<intersectImplementations<implementation, composed["implemented"]>>); | ||
type optionalizeSatisfied<base> = optionalizeKeys<base, { | ||
[k in keyof base]: undefined extends base[k] ? k : never; | ||
}[keyof base]>; | ||
type baseImplementationOf<composed extends ComposedTraits> = optionalizeSatisfied<{ | ||
[k in keyof composed["abstracted"]]: k extends keyof composed["implemented"] ? composed["implemented"][k] extends composed["abstracted"][k] ? composed["implemented"][k] | undefined : composed["implemented"][k] : composed["abstracted"][k]; | ||
}>; | ||
type omitUnambiguous<base> = Omit<base, { | ||
[k in keyof base]: undefined extends base[k] ? k : never; | ||
}[keyof base]>; | ||
type baseDisambiguationOf<traits extends readonly TraitConstructor[], implementation, composed extends ComposedTraits> = omitUnambiguous<{ | ||
[k in keyof composed["implemented"]]: k extends keyof implementation ? undefined : k extends keyof Trait ? undefined : traitsImplementingKey<traits, k> extends infer implementations extends TraitConstructor[] ? implementations["length"] extends 1 ? undefined : implementations[number] : never; | ||
}>; | ||
type traitsImplementingKey<traits extends readonly unknown[], k, result extends unknown[] = []> = traits extends readonly [ | ||
TraitConstructor<any[], infer instance, infer abstracts>, | ||
...infer tail | ||
] ? traitsImplementingKey<tail, k, k extends Exclude<keyof instance, keyof abstracts> ? [...result, traits[0]] : result> : result; | ||
export declare const hasTrait: (traitClass: Constructor) => (o: unknown) => boolean; | ||
export declare abstract class Trait { | ||
export declare abstract class Trait<abstracted extends object = {}, implemented extends object = {}> extends NoopBase<abstracted & implemented> { | ||
$abstracted: abstracted; | ||
static get [Symbol.hasInstance](): (o: unknown) => boolean; | ||
traitsOf(): readonly Function[]; | ||
traitsOf(): readonly TraitConstructor[]; | ||
} | ||
export declare abstract class DynamicTrait<t extends object> extends ShallowClone<t> { | ||
static get [Symbol.hasInstance](): (o: unknown) => boolean; | ||
traitsOf(): readonly Function[]; | ||
} | ||
export declare const compose: TraitComposition; | ||
export type compose<traits extends readonly Constructor[]> = composeRecurse<traits, [ | ||
], {}, {}>; | ||
export type composeRecurse<traits extends readonly unknown[], parameters extends readonly unknown[], statics extends {}, instance extends {}> = traits extends readonly [ | ||
abstract new (...args: infer nextArgs) => infer nextInstance, | ||
export type TraitConstructor<params extends readonly unknown[] = any[], instance = {}, abstracted = {}, statics = {}> = statics & (new (...args: params) => { | ||
$abstracted: abstracted; | ||
} & instance); | ||
export type ComposedTraits = { | ||
params: readonly unknown[]; | ||
implemented: unknown; | ||
abstracted: unknown; | ||
statics: unknown; | ||
}; | ||
export type compose<traits extends readonly TraitConstructor[]> = composeRecurse<traits, [], {}, {}, {}>; | ||
type intersectImplementations<l, r> = { | ||
[k in keyof l]: k extends keyof r ? l[k] extends (...args: infer lArgs) => infer lReturn ? r[k] extends (...args: infer rArgs) => infer rReturn ? (...args: intersectParameters<lArgs, rArgs>) => lReturn & rReturn : l[k] & r[k] : l[k] & r[k] : l[k]; | ||
} & Omit<r, keyof l>; | ||
type composeRecurse<traits extends readonly unknown[], params extends readonly unknown[], implemented, abstracted, statics> = traits extends readonly [ | ||
TraitConstructor<infer nextParams, infer nextInstance, infer nextAbstracted, infer nextStatics>, | ||
...infer tail | ||
] ? composeRecurse<tail, intersectParameters<parameters, nextArgs>, statics & { | ||
[k in keyof traits[0]]: traits[0][k]; | ||
}, instance & nextInstance> : statics & (abstract new (...args: parameters) => instance); | ||
] ? composeRecurse<tail, intersectParameters<params, nextParams>, intersectImplementations<implemented, Omit<nextInstance, keyof nextAbstracted>>, intersectImplementations<abstracted, nextAbstracted>, intersectImplementations<statics, nextStatics>> : { | ||
params: params; | ||
implemented: evaluate<implemented>; | ||
abstracted: evaluate<abstracted>; | ||
statics: evaluate<Omit<statics, keyof typeof Trait>>; | ||
}; | ||
export {}; | ||
//# sourceMappingURL=traits.d.ts.map |
import { hasDomain } from "./domain.js"; | ||
import { ancestorsOf } from "./objectKinds.js"; | ||
import { ShallowClone } from "./records.js"; | ||
import { NoopBase } from "./records.js"; | ||
// even though the value we attach will be identical, we use this so classes | ||
@@ -18,3 +18,4 @@ // won't be treated as instanceof a Trait | ||
}; | ||
export class Trait { | ||
// @ts-expect-error allow abstract property access | ||
export class Trait extends NoopBase { | ||
static get [Symbol.hasInstance]() { | ||
@@ -29,14 +30,3 @@ return hasTrait(this); | ||
} | ||
// @ts-expect-error see ShallowClone | ||
export class DynamicTrait extends ShallowClone { | ||
static get [Symbol.hasInstance]() { | ||
return hasTrait(this); | ||
} | ||
traitsOf() { | ||
return implementedTraits in this.constructor | ||
? this.constructor[implementedTraits] | ||
: []; | ||
} | ||
} | ||
const collectPrototypeDescriptors = (trait) => { | ||
const collectPrototypeDescriptors = (trait, disambiguation) => { | ||
let proto = trait.prototype; | ||
@@ -49,11 +39,11 @@ let result = {}; | ||
} while (proto !== Object.prototype && proto !== null); | ||
for (const k in disambiguation) { | ||
if (disambiguation[k] !== trait) { | ||
// remove keys disambiguated to resolve to other traits | ||
delete result[k]; | ||
} | ||
} | ||
return result; | ||
}; | ||
export const compose = ((...traits) => { | ||
if (traits.length === 0) { | ||
return Object; | ||
} | ||
if (traits.length === 1) { | ||
return traits[0]; | ||
} | ||
export const compose = ((...traits) => (implementation, disambiguation = {}) => { | ||
const base = function (...args) { | ||
@@ -70,3 +60,3 @@ for (const trait of traits) { | ||
// flatten and copy prototype | ||
Object.defineProperties(base.prototype, collectPrototypeDescriptors(trait)); | ||
Object.defineProperties(base.prototype, collectPrototypeDescriptors(trait, disambiguation)); | ||
if (implementedTraits in trait) { | ||
@@ -88,4 +78,6 @@ // add any ancestor traits from which the current trait was composed | ||
}); | ||
// copy implementation last since it overrides traits | ||
Object.defineProperties(base.prototype, Object.getOwnPropertyDescriptors(implementation)); | ||
return base; | ||
}); | ||
//# sourceMappingURL=traits.js.map |
{ | ||
"name": "@arktype/util", | ||
"version": "0.0.13", | ||
"version": "0.0.14", | ||
"author": { | ||
@@ -5,0 +5,0 @@ "name": "David Blass", |
import { hasDomain } from "./domain.js" | ||
import type { conform, evaluate } from "./generics.js" | ||
import type { intersectParameters } from "./intersections.js" | ||
import type { filter } from "./lists.js" | ||
import { ancestorsOf, type Constructor } from "./objectKinds.js" | ||
@@ -71,6 +70,4 @@ import { NoopBase, type optionalizeKeys } from "./records.js" | ||
? undefined | ||
: filter< | ||
traits, | ||
TraitConstructor<any[], { [_ in k]: unknown }, { [_ in k]?: never }> | ||
> extends infer implementations extends TraitConstructor[] | ||
: traitsImplementingKey<traits, k> extends infer implementations extends | ||
TraitConstructor[] | ||
? implementations["length"] extends 1 | ||
@@ -82,2 +79,19 @@ ? undefined | ||
type traitsImplementingKey< | ||
traits extends readonly unknown[], | ||
k, | ||
result extends unknown[] = [] | ||
> = traits extends readonly [ | ||
TraitConstructor<any[], infer instance, infer abstracts>, | ||
...infer tail | ||
] | ||
? traitsImplementingKey< | ||
tail, | ||
k, | ||
k extends Exclude<keyof instance, keyof abstracts> | ||
? [...result, traits[0]] | ||
: result | ||
> | ||
: result | ||
// even though the value we attach will be identical, we use this so classes | ||
@@ -184,3 +198,3 @@ // won't be treated as instanceof a Trait | ||
type TraitConstructor< | ||
export type TraitConstructor< | ||
params extends readonly unknown[] = any[], | ||
@@ -187,0 +201,0 @@ instance = {}, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
197226
87
2915