diff --git a/packages/type-traverser/example/example.ts b/packages/type-traverser/example/example.ts index cadf5bc..bbb1656 100644 --- a/packages/type-traverser/example/example.ts +++ b/packages/type-traverser/example/example.ts @@ -1,5 +1,13 @@ -import type { TraverserDefinition, ValidateTraverserTypes } from "../src"; +import type { + TraverserDefinition, + ValidateTraverserTypes, + AssertExtends, + InterfaceType, + PrimitiveType, + UnionType, +} from "../src"; import { Traverser } from "../src"; +import type { ReverseRelationshipIndentifiers } from "../src/reverseRelationshipTypes"; async function run() { /** @@ -68,6 +76,62 @@ async function run() { }; }>; + type AvatarReverseRelationshipIdentifiers = + ReverseRelationshipIndentifiers; + + const sample: AvatarReverseRelationshipIdentifiers = { + Element: ["Bender", "element"], + Bender: ["Person"], + } + + type KeysMatchingCondition = { + [K in keyof T]: T[K] extends Condition ? K : never; + }[keyof T]; + + // Condition: objects with `{ kind: "interface" }` + type InterfaceKeys = KeysMatchingCondition< + AvatarTraverserTypes, + { kind: "interface" } + >; + + + + type something = AvatarTraverserTypes[keyof AvatarTraverserTypes]; + type something2 = something extends PrimitiveType ? "cool" : never; + + type SomeInterface = { + a: { type: "1" }; + b: { type: "2" }; + c: { type: "3" }; + }; + + // type TestUnionType = AvatarTraverserTypes[keyof AvatarTraverserTypes]; + + // type MapUnion = T extends InterfaceType + // ? "interface" + // : T extends UnionType + // ? "union" + // : T extends PrimitiveType + // ? "primitive" + // : never; + + // // Example usage: + // type MappedUnion = MapUnion; // "a_mapped" | "b_mapped" | "c_mapped" + + interface; + type UnionType = "a" | "b" | "c"; + + type MapUnion = T extends "a" + ? "a_mapped" + : T extends "b" + ? "b_mapped" + : T extends "c" + ? "c_mapped" + : never; + + // Example usage: + type MappedUnion = MapUnion; // "a_mapped" | "b_mapped" | "c_mapped" + /** * Create the traverser definition */ diff --git a/packages/type-traverser/src/ReverseRelationshipTypes.ts b/packages/type-traverser/src/ReverseRelationshipTypes.ts new file mode 100644 index 0000000..475a621 --- /dev/null +++ b/packages/type-traverser/src/ReverseRelationshipTypes.ts @@ -0,0 +1,77 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { + InterfaceType, + PrimitiveType, + TraverserTypes, + UnionType, +} from "./TraverserTypes"; + +export type InterfaceReverseRelationshipIndentifier< + Types extends TraverserTypes, + ChildName extends keyof Types, + PotentialParentName extends keyof Types, + PotentialParentType extends InterfaceType, +> = { + [PropertyField in keyof PotentialParentType["properties"]]: ChildName extends PotentialParentType["properties"][PropertyField] + ? [PotentialParentName, PropertyField] + : never; +}[keyof PotentialParentType["properties"]]; + +export type UnionReverseRelationshipIndentifier< + Types extends TraverserTypes, + ChildName extends keyof Types, + PotentialParentName extends keyof Types, + PotentialParentType extends UnionType, +> = ChildName extends PotentialParentType["typeNames"] + ? [PotentialParentName] + : never; + +export type PrimitiveReverseRelationshipIndentifier< + Types extends TraverserTypes, + _ChildName extends keyof Types, + _PotentialParentName extends keyof Types, + _PotentialParentType extends PrimitiveType, +> = never; + +export type BaseReverseRelationshipIndentifier< + Types extends TraverserTypes, + ChildName extends keyof Types, + PotentialParentName extends keyof Types, +> = Types[PotentialParentName] extends InterfaceType + ? InterfaceReverseRelationshipIndentifier< + Types, + ChildName, + PotentialParentName, + Types[PotentialParentName] + > + : Types[PotentialParentName] extends UnionType + ? UnionReverseRelationshipIndentifier< + Types, + ChildName, + PotentialParentName, + Types[PotentialParentName] + > + : Types[PotentialParentName] extends PrimitiveType + ? PrimitiveReverseRelationshipIndentifier< + Types, + ChildName, + PotentialParentName, + Types[PotentialParentName] + > + : never; + +export type BaseReverseRelationshipIndentifiers< + Types extends TraverserTypes, + ChildName extends keyof Types, +> = { + [ParentName in keyof Types]: BaseReverseRelationshipIndentifier< + Types, + ChildName, + ParentName + >; +}; + +export type ParentIdentifiers< + Types extends TraverserTypes, + ChildName extends keyof Types, +> = BaseReverseRelationshipIndentifiers[keyof Types]; diff --git a/packages/type-traverser/src/Transformer.ts b/packages/type-traverser/src/Transformer.ts index 0d62555..143e66b 100644 --- a/packages/type-traverser/src/Transformer.ts +++ b/packages/type-traverser/src/Transformer.ts @@ -8,7 +8,7 @@ import type { PrimitiveReturnType, PrimitiveType, TransformerInputReturnTypes, - TraverserDefinition, + TraverserDefinitions, TraverserTypes, UnionReturnType, UnionType, @@ -38,7 +38,7 @@ export class Transformer< InputReturnTypes extends TransformerInputReturnTypes, Context = undefined, > { - private traverserDefinition: TraverserDefinition; + private traverserDefinition: TraverserDefinitions; private transformers: Transformers< Types, ApplyTransformerReturnTypesDefaults, @@ -46,7 +46,7 @@ export class Transformer< >; constructor( - traverserDefinition: TraverserDefinition, + traverserDefinition: TraverserDefinitions, transformers: TransformersInput, ) { this.traverserDefinition = traverserDefinition; diff --git a/packages/type-traverser/src/Traverser.ts b/packages/type-traverser/src/Traverser.ts index 09a046f..b514f4d 100644 --- a/packages/type-traverser/src/Traverser.ts +++ b/packages/type-traverser/src/Traverser.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { TransformerInputReturnTypes, - TraverserDefinition, + TraverserDefinitions, TraverserTypes, VisitorsInput, } from "."; @@ -12,9 +12,9 @@ export class Traverser< // eslint-disable-next-line @typescript-eslint/no-explicit-any Types extends TraverserTypes, > { - private traverserDefinition: TraverserDefinition; + private traverserDefinition: TraverserDefinitions; - constructor(traverserDefinition: TraverserDefinition) { + constructor(traverserDefinition: TraverserDefinitions) { this.traverserDefinition = traverserDefinition; } diff --git a/packages/type-traverser/src/TraverserDefinition.ts b/packages/type-traverser/src/TraverserDefinition.ts index 0630e14..aec169f 100644 --- a/packages/type-traverser/src/TraverserDefinition.ts +++ b/packages/type-traverser/src/TraverserDefinition.ts @@ -22,14 +22,17 @@ export type PrimitiveTraverserDefinition = { kind: "primitive"; }; -export type TraverserDefinition> = { - [TypeField in keyof Types]: Types[TypeField] extends InterfaceType< - keyof Types - > - ? InterfaceTraverserDefinition - : Types[TypeField] extends UnionType - ? UnionTraverserDefinition - : Types[TypeField] extends PrimitiveType - ? PrimitiveTraverserDefinition - : never; +export type TraverserDefinition< + Types extends TraverserTypes, + TypeField extends keyof Types, +> = Types[TypeField] extends InterfaceType + ? InterfaceTraverserDefinition + : Types[TypeField] extends UnionType + ? UnionTraverserDefinition + : Types[TypeField] extends PrimitiveType + ? PrimitiveTraverserDefinition + : never; + +export type TraverserDefinitions> = { + [TypeField in keyof Types]: TraverserDefinition; }; diff --git a/packages/type-traverser/src/Visitor.ts b/packages/type-traverser/src/Visitor.ts index de74556..781e38b 100644 --- a/packages/type-traverser/src/Visitor.ts +++ b/packages/type-traverser/src/Visitor.ts @@ -8,7 +8,7 @@ import type { PrimitiveType, PrimitiveVisitorDefinition, PrimitiveVisitorInputDefinition, - TraverserDefinition, + TraverserDefinitions, TraverserTypes, UnionType, UnionVisitorDefinition, @@ -27,11 +27,11 @@ export class Visitor< Types extends TraverserTypes, Context = undefined, > { - private traverserDefinition: TraverserDefinition; + private traverserDefinition: TraverserDefinitions; private visitors: Visitors; constructor( - traverserDefinition: TraverserDefinition, + traverserDefinition: TraverserDefinitions, visitors: VisitorsInput, ) { this.traverserDefinition = traverserDefinition; diff --git a/packages/type-traverser/src/instanceGraph/InstanceGraph.ts b/packages/type-traverser/src/instanceGraph/InstanceGraph.ts new file mode 100644 index 0000000..ddd4ab1 --- /dev/null +++ b/packages/type-traverser/src/instanceGraph/InstanceGraph.ts @@ -0,0 +1,39 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { MultiMap } from "../transformerSubTraversers/util/MultiMap"; +import type { TraverserTypes } from "../TraverserTypes"; +import type { InstanceNode } from "./nodes/InstanceNode"; +import { + createInstanceNodeFor, + type InstanceNodeFor, +} from "./nodes/createInstanceNodeFor"; +import type { TraverserDefinitions } from "../TraverserDefinition"; + +export class InstanceGraph> { + protected objectMap: MultiMap< + object, + keyof Types, + InstanceNode + > = new MultiMap(); + public readonly traverserDefinitions: TraverserDefinitions; + + constructor(traverserDefinitions: TraverserDefinitions) { + this.traverserDefinitions = traverserDefinitions; + } + + getNodeFor( + instance: unknown, + typeName: TypeName, + ): InstanceNodeFor { + let potentialNode; + // Skip the cache for Primitive Nodes + if ( + this.traverserDefinitions[typeName].kind !== "primitive" && + typeof instance === "object" && + instance != null + ) { + potentialNode = this.objectMap.get(instance, typeName); + } + if (potentialNode) return potentialNode as InstanceNodeFor; + return createInstanceNodeFor(instance, typeName, this); + } +} diff --git a/packages/type-traverser/src/instanceGraph/nodes/InstanceNode.ts b/packages/type-traverser/src/instanceGraph/nodes/InstanceNode.ts new file mode 100644 index 0000000..8e46865 --- /dev/null +++ b/packages/type-traverser/src/instanceGraph/nodes/InstanceNode.ts @@ -0,0 +1,80 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { TraverserDefinition } from "../.."; +import type { ParentIdentifiers } from "../../reverseRelationshipTypes"; +import type { TraverserTypes } from "../../TraverserTypes"; +import type { InstanceGraph } from "../instanceGraph"; +import type { InstanceNodeFor } from "./createInstanceNodeFor"; + +export abstract class InstanceNode< + Types extends TraverserTypes, + TypeName extends keyof Types, + _Type extends Types[TypeName], +> { + readonly graph: InstanceGraph; + readonly instance: Types[TypeName]["type"]; + readonly typeName: TypeName; + protected readonly parents: Record< + string, + Set[0]]>> + >; + + constructor( + graph: InstanceGraph, + instance: Types[TypeName]["type"], + typeName: TypeName, + ) { + this.graph = graph; + this.instance = instance; + this.typeName = typeName; + this._recursivelyBuildChildren(); + } + + private getParentKey( + identifiers: ParentIdentifiers, + ): string { + return identifiers.join("|"); + } + + public _setParent>( + identifiers: Identifiers, + parentNode: InstanceNodeFor, + ) { + const parentKey = this.getParentKey(identifiers); + if (!this.parents[parentKey]) this.parents[parentKey] = new Set(); + this.parents[parentKey].add(parentNode); + } + + public parent>( + ...identifiers: Identifiers + ): InstanceNodeFor[] { + return Array.from(this.parents[this.getParentKey(identifiers)] ?? []); + } + + public allParents(): InstanceNodeFor< + Types, + ParentIdentifiers[0] + >[] { + return Object.values(this.parents) + .map((parentSet) => Array.from(parentSet)) + .flat(); + } + + public abstract _setChild(...props: any[]): void; + + public abstract child(...props: any[]): any; + + /** + * Returns all nodes that are children of this node reguardless of their edge + */ + public abstract allChildren(): InstanceNode< + Types, + keyof Types, + Types[keyof Types] + >[]; + + protected abstract _recursivelyBuildChildren(): void; + + public get traverserDefinition(): TraverserDefinition { + return this.graph.traverserDefinitions[this.typeName]; + } +} diff --git a/packages/type-traverser/src/instanceGraph/nodes/InterfaceInstanceNode.ts b/packages/type-traverser/src/instanceGraph/nodes/InterfaceInstanceNode.ts new file mode 100644 index 0000000..dd70bf6 --- /dev/null +++ b/packages/type-traverser/src/instanceGraph/nodes/InterfaceInstanceNode.ts @@ -0,0 +1,67 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { InterfaceType, TraverserTypes } from "../../TraverserTypes"; +import type { InstanceNodeFor } from "./createInstanceNodeFor"; +import { InstanceNode } from "./InstanceNode"; + +type InterfacePropertyNode< + Types extends TraverserTypes, + Type extends InterfaceType, + PropertyName extends keyof Type["properties"], +> = Type["type"][PropertyName] extends Array + ? InstanceNodeFor[] + : InstanceNodeFor; + +export class InterfaceInstanceNode< + Types extends TraverserTypes, + TypeName extends keyof Types, + Type extends InterfaceType & Types[TypeName], +> extends InstanceNode { + private children: { + [PropertyName in keyof Type["properties"]]: InterfacePropertyNode< + Types, + Type, + PropertyName + >; + }; + + public _setChild( + propertyName: PropertyName, + child: InterfacePropertyNode, + ): void { + this.children[propertyName] = child; + } + + public child( + propertyName: PropertyName, + ): InterfacePropertyNode { + return this.children[propertyName]; + } + + public allChildren(): InstanceNodeFor< + Types, + Types[Type["properties"][keyof Type["properties"]]] + >[] { + return Object.values(this.children).flat(); + } + + public _recursivelyBuildChildren() { + Object.entries(this.instance).forEach( + ([propertyName, value]: [keyof Type["properties"], unknown]) => { + const initChildNode = (val: unknown) => { + const node = this.graph.getNodeFor(val, this.typeName); + // I know it's bad, I just can't figure out what's wrong with this type + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + node._setParent([this.typeName, propertyName], this); + return node; + }; + const childNode = ( + Array.isArray(value) + ? value.map((val) => initChildNode(val)) + : initChildNode(value) + ) as InterfacePropertyNode; + this._setChild(propertyName, childNode); + }, + ); + } +} diff --git a/packages/type-traverser/src/instanceGraph/nodes/PrimitiveInstanceNode.ts b/packages/type-traverser/src/instanceGraph/nodes/PrimitiveInstanceNode.ts new file mode 100644 index 0000000..4152c22 --- /dev/null +++ b/packages/type-traverser/src/instanceGraph/nodes/PrimitiveInstanceNode.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { PrimitiveType, TraverserTypes } from "../../TraverserTypes"; +import { InstanceNode } from "./InstanceNode"; + +export class PrimitiveInstanceNode< + Types extends TraverserTypes, + TypeName extends keyof Types, + Type extends PrimitiveType & Types[TypeName], +> extends InstanceNode { + public _setChild(): void { + return; + } + public child() { + return undefined; + } + public allChildren(): [] { + return []; + } + protected _recursivelyBuildChildren() { + return; + } +} diff --git a/packages/type-traverser/src/instanceGraph/nodes/UnionInstanceNode.ts b/packages/type-traverser/src/instanceGraph/nodes/UnionInstanceNode.ts new file mode 100644 index 0000000..586390a --- /dev/null +++ b/packages/type-traverser/src/instanceGraph/nodes/UnionInstanceNode.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { TraverserTypes, UnionType } from "../../TraverserTypes"; +import type { InstanceNodeFor } from "./createInstanceNodeFor"; +import { InstanceNode } from "./InstanceNode"; + +export class UnionInstanceNode< + Types extends TraverserTypes, + TypeName extends keyof Types, + Type extends UnionType & Types[TypeName], +> extends InstanceNode { + private childNode: InstanceNodeFor | undefined; + + public _setChild(child: InstanceNodeFor): void { + this.childNode = child; + } + + public child(): InstanceNodeFor | undefined { + return this.childNode; + } + + public allChildren(): InstanceNode[] { + return this.childNode ? [this.childNode] : []; + } + protected _recursivelyBuildChildren() { + // TODO + } +} diff --git a/packages/type-traverser/src/instanceGraph/nodes/createInstanceNodeFor.ts b/packages/type-traverser/src/instanceGraph/nodes/createInstanceNodeFor.ts new file mode 100644 index 0000000..c5d3677 --- /dev/null +++ b/packages/type-traverser/src/instanceGraph/nodes/createInstanceNodeFor.ts @@ -0,0 +1,52 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { + InterfaceType, + PrimitiveType, + TraverserTypes, + UnionType, +} from "../../TraverserTypes"; +import type { InstanceGraph } from "../instanceGraph"; +import { InterfaceInstanceNode } from "./InterfaceInstanceNode"; +import { PrimitiveInstanceNode } from "./PrimitiveInstanceNode"; +import { UnionInstanceNode } from "./UnionInstanceNode"; + +export type InstanceNodeFor< + Types extends TraverserTypes, + TypeName extends keyof Types, +> = Types[TypeName] extends InterfaceType + ? InterfaceInstanceNode + : Types[TypeName] extends UnionType + ? UnionInstanceNode + : Types[TypeName] extends PrimitiveType + ? PrimitiveInstanceNode + : never; + +export function createInstanceNodeFor< + Types extends TraverserTypes, + TypeName extends keyof Types, +>( + instance: unknown, + typeName: TypeName, + graph: InstanceGraph, +): InstanceNodeFor { + switch (graph.traverserDefinitions[typeName].kind) { + case "interface": + return new InterfaceInstanceNode( + graph, + instance, + typeName, + ) as InstanceNodeFor; + case "union": + return new UnionInstanceNode( + graph, + instance, + typeName, + ) as InstanceNodeFor; + case "primitive": + return new PrimitiveInstanceNode( + graph, + instance, + typeName, + ) as InstanceNodeFor; + } +} diff --git a/packages/type-traverser/src/transformerSubTraversers/util/MultiMap.ts b/packages/type-traverser/src/transformerSubTraversers/util/MultiMap.ts index e6790ae..583541f 100644 --- a/packages/type-traverser/src/transformerSubTraversers/util/MultiMap.ts +++ b/packages/type-traverser/src/transformerSubTraversers/util/MultiMap.ts @@ -1,3 +1,7 @@ +/** + * A Multi-Map is a map between the tuple of two items and a value + */ + /* eslint-disable @typescript-eslint/no-explicit-any */ export class MultiMap { private map: Map> = new Map(); diff --git a/packages/type-traverser/src/transformerSubTraversers/util/MultiSet.ts b/packages/type-traverser/src/transformerSubTraversers/util/MultiSet.ts index 22e9ff2..5977bfb 100644 --- a/packages/type-traverser/src/transformerSubTraversers/util/MultiSet.ts +++ b/packages/type-traverser/src/transformerSubTraversers/util/MultiSet.ts @@ -1,3 +1,7 @@ +/** + * A Multi-Set is a set where two items occupy a unique spot + */ + export class MultiSet { private map: Map> = new Map(); // eslint-disable-next-line @typescript-eslint/no-inferrable-types diff --git a/packages/type-traverser/src/transformerSubTraversers/util/transformerSubTraverserTypes.ts b/packages/type-traverser/src/transformerSubTraversers/util/transformerSubTraverserTypes.ts index ac0dda2..cd5c9a7 100644 --- a/packages/type-traverser/src/transformerSubTraversers/util/transformerSubTraverserTypes.ts +++ b/packages/type-traverser/src/transformerSubTraversers/util/transformerSubTraverserTypes.ts @@ -4,7 +4,7 @@ import type { BaseTraverserTypes, KeyTypes, TransformerReturnTypes, - TraverserDefinition, + TraverserDefinitions, TraverserTypes, } from "../.."; import type { Transformers } from "../../Transformers"; @@ -30,7 +30,7 @@ export interface TransformerSubTraverserGlobals< ReturnTypes extends TransformerReturnTypes, Context, > { - traverserDefinition: TraverserDefinition; + traverserDefinition: TraverserDefinitions; transformers: Transformers; executingPromises: TransformerSubTraverserExecutingPromises; circularDependencyAwaiter: CircularDepenedencyAwaiter; diff --git a/packages/type-traverser/src/visitorSubTraversers/util/visitorSubTraverserTypes.ts b/packages/type-traverser/src/visitorSubTraversers/util/visitorSubTraverserTypes.ts index fcc62f6..82ccfc3 100644 --- a/packages/type-traverser/src/visitorSubTraversers/util/visitorSubTraverserTypes.ts +++ b/packages/type-traverser/src/visitorSubTraversers/util/visitorSubTraverserTypes.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { BaseTraverserTypes, - TraverserDefinition, + TraverserDefinitions, TraverserTypes, Visitors, } from "../.."; @@ -22,7 +22,7 @@ export interface VisitorSubTraverserGlobals< Types extends TraverserTypes, Context, > { - traverserDefinition: TraverserDefinition; + traverserDefinition: TraverserDefinitions; visitors: Visitors; visitedObjects: MultiSet; context: Context;