feat: TypeScript typings for Dispatcher implementations
A first pass at specifying some implementations in TypeScript that actually compiles. It doesn't do anything, as installing types and operation specifications are currently dummy operations, but they are all invoked.
This commit is contained in:
parent
2a9039ac67
commit
ccc6153786
1
src/Complex/all.ts
Normal file
1
src/Complex/all.ts
Normal file
@ -0,0 +1 @@
|
||||
export * as Complex from './native.js'
|
1
src/Complex/native.ts
Normal file
1
src/Complex/native.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './type.js'
|
30
src/Complex/type.ts
Normal file
30
src/Complex/type.ts
Normal file
@ -0,0 +1,30 @@
|
||||
/// <reference path="../numbers/type.ts">
|
||||
import {joinTypes, typeOfDependency, Dependency} from '../core/Dispatcher.js'
|
||||
|
||||
export type Complex<T> = {re: T; im: T;}
|
||||
|
||||
export const Complex_type = {
|
||||
test: <T>(dep: {testT: (z: unknown) => z is T}) =>
|
||||
(z: unknown): z is Complex<T> =>
|
||||
typeof z === 'object' && 're' in z && 'im' in z
|
||||
&& dep.testT(z.re) && dep.testT(z.im),
|
||||
infer: (dep: typeOfDependency) =>
|
||||
(z: Complex<unknown>) => joinTypes(dep.typeOf(z.re), dep.typeOf(z.im)),
|
||||
from: {
|
||||
T: <T>(dep: Dependency<'zero', [T]>) => (t: T) =>
|
||||
({re: t, im: dep.zero(t)}),
|
||||
Complex: <U,T>(dep: {convert: (from: U) => T}) =>
|
||||
(z: Complex<U>) => ({re: dep.convert(z.re), im: dep.convert(z.im)})
|
||||
}
|
||||
}
|
||||
|
||||
export const complex_1 = <T>(dep: Dependency<'zero', [T]>) =>
|
||||
(t: T) => ({re: t, im: dep.zero(t)})
|
||||
export const complex_2 = <T>(t: T, u: T) => ({re: t, im: u})
|
||||
|
||||
declare module "../core/Dispatcher" {
|
||||
interface ImplementationTypes {
|
||||
complex_1_Complex: typeof complex_1
|
||||
complex_2_Complex: typeof complex_2
|
||||
}
|
||||
}
|
2
src/all.ts
Normal file
2
src/all.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './numbers/all.js'
|
||||
export * from './Complex/all.js'
|
@ -1,33 +0,0 @@
|
||||
import {Specifications, joinTypes, typeOfDependency} from '../core/Dispatcher'
|
||||
|
||||
export type Complex<T> = {re: T; im: T;}
|
||||
|
||||
declare module 'Dispatcher' {
|
||||
namespace Specifications {
|
||||
export class ComplexSpecifications {}
|
||||
|
||||
namespace ComplexSpecifications {
|
||||
export class Complex_type<T> {
|
||||
static test = (testT: (z: unknown) => z is T) =>
|
||||
(z: unknown): z is Complex<T> =>
|
||||
typeof z === 'object'
|
||||
&& 're' in z && 'im' in z
|
||||
&& testT(z.re) && testT(z.im);
|
||||
static infer = (dep: typeOfDependency) =>
|
||||
(z: Complex<unknown>) =>
|
||||
joinTypes(dep.typeOf(z.re), dep.typeOf(z.im));
|
||||
static from = {
|
||||
T: (dep: ImplementationType<'zero', [T]>) => (t: T) =>
|
||||
({re: t, im: dep.zero(t)}),
|
||||
Complex: <U>(convert: (from: U) => T) =>
|
||||
(z: Complex<U>) => ({re: convert(z.re), im: convert(z.im)})
|
||||
};
|
||||
}
|
||||
export const complex_1 = <T>(dep: DependencyType<'zero', [T]>) =>
|
||||
(t: T) => ({re: t, im: dep.zero(t)})
|
||||
export const complex_2 = <T>(t: T, u: T) => ({re: t, im: u})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {Specifications}
|
@ -2,6 +2,6 @@ export type Config = {
|
||||
predictable: boolean
|
||||
}
|
||||
|
||||
export type ConfigDependency = {
|
||||
export type configDependency = {
|
||||
config: Config
|
||||
}
|
||||
|
@ -5,11 +5,13 @@
|
||||
* for specific types (including their own).
|
||||
*/
|
||||
|
||||
// First helper types and functions for the Dispatcher
|
||||
|
||||
type TypeName = string
|
||||
type Parameter = TypeName
|
||||
type Signature = Parameter[]
|
||||
|
||||
export class Specifications {}
|
||||
export interface ImplementationTypes {}
|
||||
export type typeOfDependency = {typeOf: (x: unknown) => TypeName}
|
||||
|
||||
//dummy implementation for now
|
||||
@ -18,64 +20,46 @@ export function joinTypes(a: TypeName, b: TypeName) {
|
||||
return 'any'
|
||||
}
|
||||
|
||||
// Will need to build this up. Need to start with looping through the keys of
|
||||
// values of keys, and filtering ones that start with Name, then add in
|
||||
// checking the types.
|
||||
|
||||
// Some relevant stuff that worked in the playground:
|
||||
// type KeysMatching<T, N, V> = {[K in keyof T]-?: T[K] extends V ? K extends `${string}${N}${string}` ? K : never : never}[keyof T];
|
||||
// type ValuesMatching<T, V> = {[K in keyof T]: T[K] extends V ? T[K] : never}[keyof T]
|
||||
|
||||
// type SubKeysMatching<T, N, V> = {[K in keyof T]: KeysMatching<T[K], N, V>}[keyof T]
|
||||
// type SubValuesMatching<T, V> = {[K in keyof T]: ValuesMatching<T[K], V>}[keyof T]
|
||||
|
||||
|
||||
// let trial: SubKeysMatching<typeof Bar, 'ng', number | boolean> = 'strange'
|
||||
// let valtrial: SubValuesMatching<typeof Bar, number> = 3
|
||||
|
||||
// type MyFunc = (...args: [string, number]) => any
|
||||
|
||||
// Selecting the proper key for arguments [string, number] is working
|
||||
// let key: KeysMatching<typeof Foo, 'a', MyFunc > = 'bar' // OK, and 'baz' here does fail, as desired
|
||||
|
||||
// The above should have all of the ingredients.
|
||||
type DependenciesType = Record<String, Function>
|
||||
/**
|
||||
* Build up to Dependency type lookup
|
||||
*/
|
||||
type DependenciesType = Record<string, Function>
|
||||
|
||||
type FinalShape<FuncType> =
|
||||
FuncType extends (arg: DependenciesType) => Function
|
||||
? ReturnType<FuncType> : FuncType
|
||||
|
||||
type BeginsWith<Name> = `${Name}${string}`
|
||||
type BeginsWith<Name extends string> = `${Name}${string}`
|
||||
|
||||
type ImmediateDependency<Ob, Name, ParamTuple> =
|
||||
type DependencyTypes<Ob, Name extends string, Params extends unknown[]> =
|
||||
{[K in keyof Ob]: K extends BeginsWith<Name>
|
||||
? FinalShape<Ob[K]> extends (...args: ParamTuple) => any
|
||||
? FinalShape<Ob[K]> extends (...args: Params) => any
|
||||
? FinalShape<Ob[K]>
|
||||
: never
|
||||
: never}[keyof Ob]
|
||||
: never}
|
||||
|
||||
export type Dependency<Name extends string, Params extends unknown[]> =
|
||||
{[N in Name]:
|
||||
DependencyTypes<ImplementationTypes, N, Params>[keyof ImplementationTypes]}
|
||||
|
||||
type SpecType = typeof Specifications
|
||||
|
||||
export type ImplementationDependency<Name, ParamTuple> =
|
||||
{[S in keyof SpecType]:
|
||||
ImmediateDependency<SpecType[S], Name, ParamTuple>}[keyof SpecType]
|
||||
// Now types used in the Dispatcher class itself
|
||||
|
||||
type TypeSpecification = {
|
||||
before?: TypeName[],
|
||||
test: ((x: unknown) => boolean)
|
||||
| ((d: DependenciesType) => (x: unknown) => boolean),
|
||||
from: Record<Typename, Function>,
|
||||
from: Record<TypeName, Function>,
|
||||
infer?: (d: DependenciesType) => (z: unknown) => TypeName
|
||||
}
|
||||
|
||||
type SpecObject = Record<string, Function | TypeSpecification>
|
||||
export type SpecifcationsGroup = Record<string, SpecObject>
|
||||
type SpecificationsGroup = Record<string, SpecObject>
|
||||
|
||||
export class Dispatcher {
|
||||
installSpecification(
|
||||
name: string,
|
||||
signature: Signature,
|
||||
returns: Type,
|
||||
returns: TypeName,
|
||||
dependencies: Record<string, Signature>,
|
||||
behavior: Function // possible todo: constrain this type based
|
||||
// on the signature, return type, and dependencies. Not sure if
|
||||
@ -89,19 +73,21 @@ export class Dispatcher {
|
||||
//TODO: implement me
|
||||
}
|
||||
constructor(collection: SpecificationsGroup) {
|
||||
for (key in collection) {
|
||||
for (const key in collection) {
|
||||
console.log('Working on', key)
|
||||
for (identifier in collection[key]) {
|
||||
for (const identifier in collection[key]) {
|
||||
console.log('Handling', key, ':', identifier)
|
||||
const parts = identifier.split('_')
|
||||
if (parts[parts.length - 1] === 'type') {
|
||||
parts.pop()
|
||||
const name = parts.join('_')
|
||||
installType(name, collection[key][identifier])
|
||||
this.installType(
|
||||
name, collection[key][identifier] as TypeSpecification)
|
||||
} else {
|
||||
const name = parts[0]
|
||||
installSpecification(
|
||||
name, ['dunno'], 'unsure', {}, collection[key][identifier])
|
||||
this.installSpecification(
|
||||
name, ['dunno'], 'unsure', {},
|
||||
collection[key][identifier] as Function)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import Dispatcher from 'core/Dispatcher'
|
||||
import Complex from 'complex/type'
|
||||
import Specifications from 'number/arithmetic'
|
||||
import {Dispatcher} from './core/Dispatcher.js'
|
||||
import * as Specifications from './all.js'
|
||||
|
||||
export default new Dispatcher(Specifications)
|
||||
|
@ -1,29 +0,0 @@
|
||||
import Specifications from './type'
|
||||
import configDependency from '../core/Config'
|
||||
/// <reference path="../complex/type.ts">
|
||||
|
||||
declare module 'Dispatcher' {
|
||||
namespace Specifications {
|
||||
namespace NumberSpecifications {
|
||||
export const add = (a: number, b: number) => a + b
|
||||
export const unaryMinus = (a: number) => -a
|
||||
export const subtract = (a: number, b: number) => a - b
|
||||
export const multiply = (a: number, b: number) => a * b
|
||||
export const divide = (a: number, b: number) => a / b
|
||||
export const sqrt =
|
||||
(dep: configDependency
|
||||
& ImplementationDependency<'complex', [number,number]>) => {
|
||||
if (dep.config.predictable || !dep.complex) {
|
||||
return (a: number) => isNaN(n) ? NaN : Math.sqrt(n)
|
||||
}
|
||||
return (a: number) => {
|
||||
if (isNaN(n)) return NaN
|
||||
if (n >= 0) return Math.sqrt(n)
|
||||
return dep.complex(0, Math.sqrt(unaryMinus(n)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {Specifications}
|
@ -1,17 +0,0 @@
|
||||
import Specifications from '../core/Dispatcher'
|
||||
|
||||
declare module 'Dispatcher' {
|
||||
namespace Specifications {
|
||||
export class NumberSpecifications {}
|
||||
namespace NumberSpecifications {
|
||||
export const number_type = {
|
||||
before: ['Complex'],
|
||||
test: (n: unknown): n is number => typeof n === 'number',
|
||||
from: {string: s => +s}
|
||||
}
|
||||
export const zero = (a: number) => 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {Specifications}
|
1
src/numbers/all.ts
Normal file
1
src/numbers/all.ts
Normal file
@ -0,0 +1 @@
|
||||
export * as numbers from './native.js'
|
21
src/numbers/arithmetic.ts
Normal file
21
src/numbers/arithmetic.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/// <reference path="../Complex/type.ts">
|
||||
import {configDependency} from '../core/Config.js'
|
||||
import {Dependency, ImplementationTypes} from '../core/Dispatcher.js'
|
||||
|
||||
export const add = (a: number, b: number) => a + b
|
||||
export const unaryMinus = (a: number) => -a
|
||||
export const subtract = (a: number, b: number) => a - b
|
||||
export const multiply = (a: number, b: number) => a * b
|
||||
export const divide = (a: number, b: number) => a / b
|
||||
export const sqrt =
|
||||
(dep: configDependency
|
||||
& Dependency<'complex', [number, number]>) => {
|
||||
if (dep.config.predictable || !dep.complex) {
|
||||
return (a: number) => isNaN(a) ? NaN : Math.sqrt(a)
|
||||
}
|
||||
return (a: number) => {
|
||||
if (isNaN(a)) return NaN
|
||||
if (a >= 0) return Math.sqrt(a)
|
||||
return dep.complex(0, Math.sqrt(unaryMinus(a)))
|
||||
}
|
||||
}
|
2
src/numbers/native.ts
Normal file
2
src/numbers/native.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './type.js'
|
||||
export * from './arithmetic.js'
|
13
src/numbers/type.ts
Normal file
13
src/numbers/type.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export const number_type = {
|
||||
before: ['Complex'],
|
||||
test: (n: unknown): n is number => typeof n === 'number',
|
||||
from: {string: s => +s}
|
||||
}
|
||||
|
||||
export const zero = (a: number) => 0
|
||||
|
||||
declare module "../core/Dispatcher" {
|
||||
interface ImplementationTypes {
|
||||
zero_numbers: typeof zero
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user