diff --git a/README.md b/README.md index a8d0201..9277561 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ Roadmap: 1. Install over.ts and get an example of add with number and bigint implementations working with it. [DONE] 2. Use the builder pattern to get add working with its implmentations defined incrementally before producing the final overload. [Didn't quite work the way we wnated, but maybe we can do an alternative later.] 3. Make a version of over.ts, call it util/overload.ts, that takes an array of implementations without redundant type annotation. [DONE] -4. Improve that version of overload with rtti to select the implementation without the implementations having to throw errors. [DONE] -5. Use the builder pattern to get a single object with both an add and a negate method, with both defined incrementally, working. [DONE] +4. Improve that version of overload with rtti to select the implementation without the implementations having to throw errors. [IN PROGRESS] +3. Use the builder pattern to get a single object with both an add and a negate method, with both defined incrementally, working. 4. Incorporate a subtract method that works on numbers and bigint by separate definitions but with dependencies on add and negate. 5. Incorporate a subtract method that works with one generic implementation that works for both number and bigint with dependencies on add and negate. 5. Attempt to eliminate redundant specification of implementation signatures. diff --git a/package.json5 b/package.json5 index f41f4b6..6c809b4 100644 --- a/package.json5 +++ b/package.json5 @@ -5,7 +5,6 @@ main: 'index.js', scripts: { test: 'echo "Error: no test specified" && exit 1', - build: 'ttsc -b', }, keywords: [ 'math', @@ -19,13 +18,6 @@ license: 'Apache-2.0', dependencies: { 'over.ts': 'github:m93a/over.ts', - 'reflect-metadata': '^0.1.13', typescript: '^4.8.2', - 'typescript-rtti': '^0.8.2', - }, - devDependencies: { - '@types/node': '^18.7.21', - 'ts-node': '^10.9.1', - ttypescript: '^1.5.13', }, } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7f11d21..f729120 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,204 +1,20 @@ lockfileVersion: 5.4 specifiers: - '@types/node': ^18.7.21 over.ts: github:m93a/over.ts - reflect-metadata: ^0.1.13 - ts-node: ^10.9.1 - ttypescript: ^1.5.13 typescript: ^4.8.2 - typescript-rtti: ^0.8.2 dependencies: over.ts: github.com/m93a/over.ts/0fd6e18afd4ca5a23c9e09d1fcd6b7357b642247 - reflect-metadata: 0.1.13 typescript: 4.8.2 - typescript-rtti: 0.8.2_jbmzfhhdwyaatac7voo5jihpea - -devDependencies: - '@types/node': 18.7.21 - ts-node: 10.9.1_xxlomja3uhaizt5vuokco7nale - ttypescript: 1.5.13_s5ojjbx2isjkawqptqpitvy25q packages: - /@cspotcode/source-map-support/0.8.1: - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - dev: true - - /@jridgewell/resolve-uri/3.1.0: - resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} - engines: {node: '>=6.0.0'} - dev: true - - /@jridgewell/sourcemap-codec/1.4.14: - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - dev: true - - /@jridgewell/trace-mapping/0.3.9: - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 - dev: true - - /@tsconfig/node10/1.0.9: - resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} - dev: true - - /@tsconfig/node12/1.0.11: - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - dev: true - - /@tsconfig/node14/1.0.3: - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - dev: true - - /@tsconfig/node16/1.0.3: - resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} - dev: true - - /@types/node/18.7.21: - resolution: {integrity: sha512-rLFzK5bhM0YPyCoTC8bolBjMk7bwnZ8qeZUBslBfjZQou2ssJdWslx9CZ8DGM+Dx7QXQiiTVZ/6QO6kwtHkZCA==} - dev: true - - /acorn-walk/8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} - engines: {node: '>=0.4.0'} - dev: true - - /acorn/8.8.0: - resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /arg/4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true - - /create-require/1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true - - /diff/4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: true - - /function-bind/1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - dev: true - - /has/1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.1 - dev: true - - /is-core-module/2.10.0: - resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==} - dependencies: - has: 1.0.3 - dev: true - - /make-error/1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true - - /path-parse/1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true - - /reflect-metadata/0.1.13: - resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} - dev: false - - /resolve/1.22.1: - resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} - hasBin: true - dependencies: - is-core-module: 2.10.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: true - - /supports-preserve-symlinks-flag/1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - dev: true - - /ts-node/10.9.1_xxlomja3uhaizt5vuokco7nale: - resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.3 - '@types/node': 18.7.21 - acorn: 8.8.0 - acorn-walk: 8.2.0 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 4.8.2 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - dev: true - - /ttypescript/1.5.13_s5ojjbx2isjkawqptqpitvy25q: - resolution: {integrity: sha512-KT/RBfGGlVJFqEI8cVvI3nMsmYcFvPSZh8bU0qX+pAwbi7/ABmYkzn7l/K8skw0xmYjVCoyaV6WLsBQxdadybQ==} - hasBin: true - peerDependencies: - ts-node: '>=8.0.2' - typescript: '>=3.2.2' - dependencies: - resolve: 1.22.1 - ts-node: 10.9.1_xxlomja3uhaizt5vuokco7nale - typescript: 4.8.2 - dev: true - - /typescript-rtti/0.8.2_jbmzfhhdwyaatac7voo5jihpea: - resolution: {integrity: sha512-MZUHMX+Up1+7dYaAcOYSTxKc4PNLNXhuXYBkIzEKvQvxJ6xp7wcjTtiIYmB9+RJxRH5/9phZLqKTMpy20aQ4Aw==} - engines: {node: '>=10'} - peerDependencies: - reflect-metadata: ^0.1.13 - typescript: '4.6' - dependencies: - reflect-metadata: 0.1.13 - typescript: 4.8.2 - dev: false - /typescript/4.8.2: resolution: {integrity: sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==} engines: {node: '>=4.2.0'} hasBin: true - - /v8-compile-cache-lib/3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - dev: true - - /yn/3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - dev: true + dev: false github.com/m93a/over.ts/0fd6e18afd4ca5a23c9e09d1fcd6b7357b642247: resolution: {tarball: https://codeload.github.com/m93a/over.ts/tar.gz/0fd6e18afd4ca5a23c9e09d1fcd6b7357b642247} diff --git a/src/steps/five.ts b/src/steps/five.ts deleted file mode 100644 index 06254b5..0000000 --- a/src/steps/five.ts +++ /dev/null @@ -1,26 +0,0 @@ -import 'reflect-metadata' -import {reflect} from 'typescript-rtti' -import {merge, overloadValues} from '../util/overload.js' - -const numImps = { - add: [(x: number, y: number) => x + y], - negate: [(x: number) => -x] -} as const - -const strImps = { - add: [(x: string, y: string) => x + ', ' + y], - negate: [(x: string) => 'NOT ' + x] -} as const - -const merger = merge(numImps) -const mathImps = merger.with(strImps).imps() - -const math = overloadValues(mathImps) - -console.log(math.add(1.5, 2.5)) -console.log(math.add('One and a half', 'Two and a half')) -console.log(math.negate(3.5)) -console.log(math.negate('Three and a half')) - -//@ts-expect-error -console.log(math.add(1.5, 'Two and a half')) diff --git a/src/steps/four.ts b/src/steps/four.ts deleted file mode 100644 index 688f2bb..0000000 --- a/src/steps/four.ts +++ /dev/null @@ -1,26 +0,0 @@ -import 'reflect-metadata' -import overload from '../util/overload.js' - -const addImps = [ - (x: number, y: number) => { - if (typeof x === 'number' && typeof y === 'number') return x + y - throw new TypeError('Can only add numbers') - }, - (x: string, y: string) => { - if (typeof x === 'string' && typeof y === 'string') { - return 'Yay' + x + y - } - throw new TypeError('or strings') - }, - (x: bigint, final?: string) => x.toString() + final -] as const - -const adder = overload(addImps) - -console.log(adder(1, 2)) -console.log(adder('a', 'b')) -console.log(adder(2n)) - -// And make sure typescript complains on signatures not covered by an imp: -//@ts-expect-error -console.log(adder(2n, 4n)) diff --git a/src/steps/one.ts b/src/steps/one.ts index 0ef25f4..a9f0eab 100644 --- a/src/steps/one.ts +++ b/src/steps/one.ts @@ -1,4 +1,4 @@ -import { useTypes } from '../../node_modules/over.ts/src/index.js'; +import { useTypes } from 'over.ts/src/index.js'; const types = { number: (x: unknown): x is number => typeof x === 'number', diff --git a/src/steps/three.ts b/src/steps/three.ts index 05e563c..e76253a 100644 --- a/src/steps/three.ts +++ b/src/steps/three.ts @@ -6,10 +6,7 @@ const adder = overload([ throw new TypeError('Can only add numbers') }, (x: string, y: string) => 'Yay' + x + y -] as const) +]) console.log(adder(1, 2)) console.log(adder('a', 'b')) -//@ts-expect-error -console.log(adder(3n, 4n)) - diff --git a/src/steps/two.ts b/src/steps/two.ts index 2080f99..6e47772 100644 --- a/src/steps/two.ts +++ b/src/steps/two.ts @@ -1,4 +1,4 @@ -import { useTypes } from 'over.ts'; +import { useTypes } from 'over.ts/src/index.js'; const types = { number: (x: unknown): x is number => typeof x === 'number', @@ -69,8 +69,6 @@ addImps.addImp('number, number -> number', (a: number, b: number) => a+b) // ima const addAll = addImps.addImp('bigint, bigint -> bigint', (a: bigint, b: bigint) => a+b) // and this is in the `bigint` file const add = overload(addAll.finalize() as Omit) // We wrap everything up elsewhere -// This should **NOT** be an error but sadly it is: -//@ts-expect-error console.log('Sum of 5 and 7 is', add(5,7)) console.log('Sum of 5n and 7n is', add(5n, 7n)) diff --git a/src/util/overload.ts b/src/util/overload.ts index 4ca62bb..71ab3fa 100644 --- a/src/util/overload.ts +++ b/src/util/overload.ts @@ -1,117 +1,17 @@ -import {reflect, CallSite} from 'typescript-rtti' - type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; export default function overload( - imps: T, callSite?: CallSite): UnionToIntersection { - const impTypes = reflect(callSite).parameters[0].elements + imps: T): UnionToIntersection { return ((...a: any[]) => { for (let i = 0; i < imps.length; ++i) { - const paramTypes = imps[i].functionType.parameters - || impTypes[i].type.parameters - let match = true - const haveArgs = a.length - let onArg = 0 - for (const param of paramTypes) { - if (param.isRest) { - // All the rest of the arguments must be of param's type - match = a.slice(onArg).every( - arg => param.type.matchesValue(arg)) - break - } - if (onArg === haveArgs) { - // We've used all of the arguments, so better be optional - match = param.isOptional - break - } - // This argument must match this param's type and both are used - match = param.type.matchesValue(a[onArg]) - onArg += 1 - if (!match) break + try { + const val = imps[i](...a) + return val + } catch { } - // Make sure we used all the arguments - match &&= (onArg == haveArgs) - if (match) return imps[i](...a) } - throw new TypeError( - `Actual arguments ${a} of type ${a.map(arg => typeof arg)} ` - + 'did not match any implementation') }) } - -type AVO = Record - -type MergeArrayValues = - Pick> - & Pick> - & { [P in (keyof L & keyof R)]: [...L[P], ...R[P]] } - -function mergeImps( - impT: T, impU: U): MergeArrayValues { - const dummy = Object.assign({}, impT, impU) - const result = {} as AVO - for (const key in dummy) { - if (key in impT) { - if (key in impU) { - const k = key as Extract - result[k] = [...impT[k], ...impU[k]] - } else { - result[key] = impT[key] - } - } else { - result[key] = impU[key] - } - } - return result as MergeArrayValues -} - -class ImpMerger { - implementations: T - constructor(imps: T) { - this.implementations = imps - } - with( - moreImps: U, callSite?: CallSite): ImpMerger> { - const Utype = reflect(callSite).parameters[0] - // Annotate the implementations with their RTTI while we have it - for (const [key, imps] of Object.entries(moreImps)) { - if (!(imps[0].functionType)) { - const tuple = Utype.members.find(m => m.name === key) - for (let i = 0; i < imps.length; ++i) { - imps[i].functionType = tuple.type.elements[i].type - } - } - } - return new ImpMerger(mergeImps(this.implementations, moreImps)) - } - imps(): T { - return this.implementations - } -} - -function merge(impsObject: T, callSite?: CallSite) { - const Ttype = reflect(callSite).parameters[0] - // Annotate the implementations with their RTTI while we have it - for (const [key, imps] of Object.entries(impsObject)) { - if (!(imps[0].functionType)) { - const tuple = Ttype.members.find(m => m.name === key) - for (let i = 0; i < imps.length; ++i) { - imps[i].functionType = tuple.type.elements[i].type - } - } - } - return new ImpMerger(impsObject) -} - -type OverloadedValues = { - [P in keyof T]: UnionToIntersection} - -function overloadValues(impObject: T) : OverloadedValues { - return Object.fromEntries(Object.entries(impObject).map( - ([k, v]) => [k, overload(v)])) as OverloadedValues -} - -export {merge, mergeImps, overloadValues} diff --git a/tsconfig.json b/tsconfig.json index ac3755e..99d3a5d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,18 +1,12 @@ { "compilerOptions": { "target": "ES2022", - "baseUrl": ".", - "paths": { - "over.ts": ["node_modules/over.ts/src/index.js"], - "typescript-rtti": ["node_modules/typescript-rtti/dist/index.js"] - }, - "plugins": [{ "transform": "typescript-rtti/dist/transformer" }], - "rootDir": ".", + "moduleResolution": "node", + "rootDir": "src", "outDir": "obj" }, "include": [ - "node_modules/over.ts/src/index.ts", - "src/steps/*.ts", + "src/steps/three.ts", "src/util/*.ts" ] }