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
|
predictable: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ConfigDependency = {
|
export type configDependency = {
|
||||||
config: Config
|
config: Config
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,13 @@
|
|||||||
* for specific types (including their own).
|
* for specific types (including their own).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// First helper types and functions for the Dispatcher
|
||||||
|
|
||||||
type TypeName = string
|
type TypeName = string
|
||||||
type Parameter = TypeName
|
type Parameter = TypeName
|
||||||
type Signature = Parameter[]
|
type Signature = Parameter[]
|
||||||
|
|
||||||
export class Specifications {}
|
export interface ImplementationTypes {}
|
||||||
export type typeOfDependency = {typeOf: (x: unknown) => TypeName}
|
export type typeOfDependency = {typeOf: (x: unknown) => TypeName}
|
||||||
|
|
||||||
//dummy implementation for now
|
//dummy implementation for now
|
||||||
@ -18,64 +20,46 @@ export function joinTypes(a: TypeName, b: TypeName) {
|
|||||||
return 'any'
|
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
|
* Build up to Dependency type lookup
|
||||||
// checking the types.
|
*/
|
||||||
|
type DependenciesType = Record<string, Function>
|
||||||
// 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>
|
|
||||||
|
|
||||||
type FinalShape<FuncType> =
|
type FinalShape<FuncType> =
|
||||||
FuncType extends (arg: DependenciesType) => Function
|
FuncType extends (arg: DependenciesType) => Function
|
||||||
? ReturnType<FuncType> : FuncType
|
? 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>
|
{[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]>
|
? FinalShape<Ob[K]>
|
||||||
: never
|
: never
|
||||||
: never}[keyof Ob]
|
: never}
|
||||||
|
|
||||||
type SpecType = typeof Specifications
|
export type Dependency<Name extends string, Params extends unknown[]> =
|
||||||
|
{[N in Name]:
|
||||||
|
DependencyTypes<ImplementationTypes, N, Params>[keyof ImplementationTypes]}
|
||||||
|
|
||||||
export type ImplementationDependency<Name, ParamTuple> =
|
// Now types used in the Dispatcher class itself
|
||||||
{[S in keyof SpecType]:
|
|
||||||
ImmediateDependency<SpecType[S], Name, ParamTuple>}[keyof SpecType]
|
|
||||||
|
|
||||||
type TypeSpecification = {
|
type TypeSpecification = {
|
||||||
before?: TypeName[],
|
before?: TypeName[],
|
||||||
test: ((x: unknown) => boolean)
|
test: ((x: unknown) => boolean)
|
||||||
| ((d: DependenciesType) => (x: unknown) => boolean),
|
| ((d: DependenciesType) => (x: unknown) => boolean),
|
||||||
from: Record<Typename, Function>,
|
from: Record<TypeName, Function>,
|
||||||
infer?: (d: DependenciesType) => (z: unknown) => TypeName
|
infer?: (d: DependenciesType) => (z: unknown) => TypeName
|
||||||
}
|
}
|
||||||
|
|
||||||
type SpecObject = Record<string, Function | TypeSpecification>
|
type SpecObject = Record<string, Function | TypeSpecification>
|
||||||
export type SpecifcationsGroup = Record<string, SpecObject>
|
type SpecificationsGroup = Record<string, SpecObject>
|
||||||
|
|
||||||
export class Dispatcher {
|
export class Dispatcher {
|
||||||
installSpecification(
|
installSpecification(
|
||||||
name: string,
|
name: string,
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
returns: Type,
|
returns: TypeName,
|
||||||
dependencies: Record<string, Signature>,
|
dependencies: Record<string, Signature>,
|
||||||
behavior: Function // possible todo: constrain this type based
|
behavior: Function // possible todo: constrain this type based
|
||||||
// on the signature, return type, and dependencies. Not sure if
|
// on the signature, return type, and dependencies. Not sure if
|
||||||
@ -89,19 +73,21 @@ export class Dispatcher {
|
|||||||
//TODO: implement me
|
//TODO: implement me
|
||||||
}
|
}
|
||||||
constructor(collection: SpecificationsGroup) {
|
constructor(collection: SpecificationsGroup) {
|
||||||
for (key in collection) {
|
for (const key in collection) {
|
||||||
console.log('Working on', key)
|
console.log('Working on', key)
|
||||||
for (identifier in collection[key]) {
|
for (const identifier in collection[key]) {
|
||||||
console.log('Handling', key, ':', identifier)
|
console.log('Handling', key, ':', identifier)
|
||||||
const parts = identifier.split('_')
|
const parts = identifier.split('_')
|
||||||
if (parts[parts.length - 1] === 'type') {
|
if (parts[parts.length - 1] === 'type') {
|
||||||
parts.pop()
|
parts.pop()
|
||||||
const name = parts.join('_')
|
const name = parts.join('_')
|
||||||
installType(name, collection[key][identifier])
|
this.installType(
|
||||||
|
name, collection[key][identifier] as TypeSpecification)
|
||||||
} else {
|
} else {
|
||||||
const name = parts[0]
|
const name = parts[0]
|
||||||
installSpecification(
|
this.installSpecification(
|
||||||
name, ['dunno'], 'unsure', {}, collection[key][identifier])
|
name, ['dunno'], 'unsure', {},
|
||||||
|
collection[key][identifier] as Function)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import Dispatcher from 'core/Dispatcher'
|
import {Dispatcher} from './core/Dispatcher.js'
|
||||||
import Complex from 'complex/type'
|
import * as Specifications from './all.js'
|
||||||
import Specifications from 'number/arithmetic'
|
|
||||||
|
|
||||||
export default new Dispatcher(Specifications)
|
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