refactor: change Dispatcher class to a closure
Also starts work on typing that closure. Gets the property names right, but currently has the "unfilled" method types, rather than the returned "filled-in" function types. Not sure how to fix this.
This commit is contained in:
parent
6bfd06cafb
commit
569079e908
@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
import {parseReflectedType, ImplementationDef} from './parseReflectedType.js'
|
||||
import type {ValueIntersectionByKeyUnion} from '../interfaces/type.js'
|
||||
|
||||
// First helper types and functions for the Dispatcher
|
||||
|
||||
@ -35,50 +36,103 @@ type TypeSpecification = {
|
||||
infer?: (d: DependenciesType) => (z: unknown) => TypeName
|
||||
}
|
||||
|
||||
type SpecObject = Record<string, Function | TypeSpecification>
|
||||
type Callable = (...args: any) => any
|
||||
type SpecObject = Record<string, Callable | TypeSpecification>
|
||||
type SpecificationsGroup = Record<string, SpecObject>
|
||||
|
||||
export class Dispatcher {
|
||||
installSpecification(
|
||||
name: string,
|
||||
defn: ImplementationDef,
|
||||
behavior: Function // possible todo: constrain this type based
|
||||
// on the signature, return type, and dependencies. Not sure if
|
||||
// that's really possible, though.
|
||||
) {
|
||||
console.log('Pretending to install', name, 'with signatures')
|
||||
for (const signature of defn.fn.signatures) {
|
||||
console.log(' ', signature.args, '=>', signature.returns)
|
||||
}
|
||||
if (defn.fn.aliasOf) {
|
||||
console.log(' As an alias of', defn.fn.aliasOf)
|
||||
}
|
||||
//TODO: implement me
|
||||
}
|
||||
installType(name: TypeName, typespec: TypeSpecification) {
|
||||
console.log('Pretending to install type', name, typespec)
|
||||
//TODO: implement me
|
||||
}
|
||||
constructor(collection: SpecificationsGroup) {
|
||||
const implementations = []
|
||||
for (const key in collection) {
|
||||
console.log('Working on', key)
|
||||
for (const identifier in collection[key]) {
|
||||
const item = collection[key][identifier]
|
||||
if (typeof item === 'function') {
|
||||
implementations.push([key, identifier, item])
|
||||
} else {
|
||||
console.log('Handling type', key, ':', identifier)
|
||||
this.installType(
|
||||
item.name, collection[key][identifier] as TypeSpecification)
|
||||
}
|
||||
// Find all of the keys of an object that are functions but not aliases
|
||||
// to another operation
|
||||
type BaseOperations<Obj extends SpecObject> =
|
||||
{[K in keyof Obj]:
|
||||
Obj[K] extends Callable
|
||||
? ReturnType<Obj[K]>['aliasOf'] extends (string | undefined)
|
||||
? never
|
||||
: K extends string ? K : never
|
||||
: never
|
||||
}[keyof Obj]
|
||||
// Gather all of the operations specified in a SpecificationsGroup
|
||||
type DispatcherOperations<G extends SpecificationsGroup> =
|
||||
{[K in keyof G]: BaseOperations<G[K]>}[keyof G]
|
||||
// Get the type of a given operation in a SpecObject:
|
||||
type BaseOperationSignature<
|
||||
Obj extends SpecObject,
|
||||
Name extends BaseOperations<Obj>
|
||||
> = ValueIntersectionByKeyUnion<
|
||||
{[K in keyof Obj]:
|
||||
K extends Name
|
||||
? Obj[K]
|
||||
: Obj[K] extends Callable
|
||||
? ReturnType<Obj[K]>['aliasOf'] extends (Name | undefined)
|
||||
? Obj[K] : unknown
|
||||
: unknown
|
||||
},
|
||||
keyof Obj>
|
||||
// Get the type of a given operation in a SpecificationsGroup
|
||||
type OperationSignature<
|
||||
G extends SpecificationsGroup,
|
||||
Name extends DispatcherOperations<G>
|
||||
> = ValueIntersectionByKeyUnion<
|
||||
{[K in keyof G]:
|
||||
Name extends BaseOperations<G[K]>
|
||||
? BaseOperationSignature<G[K], Name>
|
||||
: unknown},
|
||||
keyof G>
|
||||
// Put it all together into the typing of the dispatcher
|
||||
type DispatcherInterface<G extends SpecificationsGroup> =
|
||||
{[Op in DispatcherOperations<G>]: OperationSignature<G, Op>}
|
||||
|
||||
export function createDispatcher<G extends SpecificationsGroup>(
|
||||
collection: G): DispatcherInterface<G>
|
||||
{
|
||||
const implementations = []
|
||||
const types = {} // who knows what the type of this will be
|
||||
const behaviors = {} // ditto
|
||||
for (const key in collection) {
|
||||
console.log('Working on', key)
|
||||
for (const identifier in collection[key]) {
|
||||
const item = collection[key][identifier]
|
||||
if (typeof item === 'function') {
|
||||
implementations.push([key, identifier, item])
|
||||
} else {
|
||||
console.log('Handling type', key, ':', identifier)
|
||||
installType( // In this design, installType would modify
|
||||
// types by side effect; maybe Jos prefers some other
|
||||
// factoring of this?
|
||||
types, item.name,
|
||||
collection[key][identifier] as TypeSpecification)
|
||||
}
|
||||
}
|
||||
for (const trio of implementations) {
|
||||
const [k, id, imp] = trio
|
||||
console.log('Handling implementation', id, 'from', k)
|
||||
this.installSpecification(
|
||||
id, parseReflectedType(id, imp.reflectedType), imp)
|
||||
}
|
||||
}
|
||||
for (const trio of implementations) {
|
||||
const [k, id, imp] = trio
|
||||
console.log('Handling implementation', id, 'from', k)
|
||||
installSpecification(
|
||||
behaviors, id, parseReflectedType(id, imp.reflectedType), imp)
|
||||
}
|
||||
return {} as DispatcherInterface<G>
|
||||
}
|
||||
|
||||
function installType(
|
||||
types: Object, name: TypeName, typespec: TypeSpecification)
|
||||
{
|
||||
console.log('Pretending to install type', name, typespec)
|
||||
//TODO: implement me
|
||||
}
|
||||
|
||||
function installSpecification(
|
||||
behaviors: Object, // Same issue as mentioned above with side effects...
|
||||
name: string,
|
||||
defn: ImplementationDef,
|
||||
specification: Function // possible todo: constrain this type based
|
||||
// on the signature, return type, and dependencies. Not sure if
|
||||
// that's really possible, though.
|
||||
) {
|
||||
console.log('Pretending to install', name, 'with signatures')
|
||||
for (const signature of defn.fn.signatures) {
|
||||
console.log(' ', signature.args, '=>', signature.returns)
|
||||
}
|
||||
if (defn.fn.aliasOf) {
|
||||
console.log(' As an alias of', defn.fn.aliasOf)
|
||||
}
|
||||
//TODO: implement me
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
import {inspect} from 'node:util'
|
||||
import {Dispatcher} from './core/Dispatcher.js'
|
||||
import {createDispatcher} from './core/Dispatcher.js'
|
||||
import * as Specifications from './all.js'
|
||||
|
||||
export default new Dispatcher(Specifications)
|
||||
export Specifications
|
||||
const math = createDispatcher(Specifications)
|
||||
export default math
|
||||
|
||||
console.log('Made', math.add}
|
||||
|
||||
import {Complex} from './Complex/type.js'
|
||||
import {absquare as absquare_complex} from './Complex/arithmetic.js'
|
||||
|
@ -15,7 +15,7 @@ import {$$typeToString} from 'ts-macros'
|
||||
* but that's OK, the generic parameter doesn't hurt in those cases.
|
||||
****/
|
||||
|
||||
type ValueIntersectionByKeyUnion<T, TKey extends keyof T> = {
|
||||
export type ValueIntersectionByKeyUnion<T, TKey extends keyof T> = {
|
||||
[P in TKey]: (k: T[P])=>void
|
||||
} [TKey] extends ((k: infer I)=>void) ? I : never
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
"rootDir": "./src",
|
||||
"outDir": "./build",
|
||||
"moduleResolution": "nodenext",
|
||||
"declaration": true,
|
||||
"plugins": [ {
|
||||
"transform": "ts-macros/dist/type-resolve",
|
||||
"transformProgram": true
|
||||
|
Loading…
Reference in New Issue
Block a user