feat(overload): Use typescript-rtti to select implementations
Note that the changes to module resolution cause steps one and two no longer to run because node can't find the imports, although they still compile fine.
This commit is contained in:
parent
a2d019b021
commit
b15035bfe6
@ -7,7 +7,7 @@ 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. [IN PROGRESS]
|
||||
4. Improve that version of overload with rtti to select the implementation without the implementations having to throw errors. [DONE]
|
||||
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,6 +5,7 @@
|
||||
main: 'index.js',
|
||||
scripts: {
|
||||
test: 'echo "Error: no test specified" && exit 1',
|
||||
build: 'ttsc -b',
|
||||
},
|
||||
keywords: [
|
||||
'math',
|
||||
@ -18,6 +19,13 @@
|
||||
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',
|
||||
},
|
||||
}
|
||||
|
186
pnpm-lock.yaml
186
pnpm-lock.yaml
@ -1,20 +1,204 @@
|
||||
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
|
||||
dev: false
|
||||
|
||||
/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
|
||||
|
||||
github.com/m93a/over.ts/0fd6e18afd4ca5a23c9e09d1fcd6b7357b642247:
|
||||
resolution: {tarball: https://codeload.github.com/m93a/over.ts/tar.gz/0fd6e18afd4ca5a23c9e09d1fcd6b7357b642247}
|
||||
|
26
src/steps/four.ts
Normal file
26
src/steps/four.ts
Normal file
@ -0,0 +1,26 @@
|
||||
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))
|
@ -1,4 +1,4 @@
|
||||
import { useTypes } from 'over.ts/src/index.js';
|
||||
import { useTypes } from '../../node_modules/over.ts/src/index.js';
|
||||
|
||||
const types = {
|
||||
number: (x: unknown): x is number => typeof x === 'number',
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useTypes } from 'over.ts/src/index.js';
|
||||
import { useTypes } from 'over.ts';
|
||||
|
||||
const types = {
|
||||
number: (x: unknown): x is number => typeof x === 'number',
|
||||
@ -69,6 +69,8 @@ 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<typeof addAll, 'finalize'|'addImp'>) // 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))
|
||||
|
||||
|
@ -1,17 +1,42 @@
|
||||
import {reflect, CallSite} from 'typescript-rtti'
|
||||
|
||||
type UnionToIntersection<U> =
|
||||
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void)
|
||||
? I
|
||||
: never;
|
||||
|
||||
export default function overload<T extends readonly [...any[]]>(
|
||||
imps: T): UnionToIntersection<T[number]> {
|
||||
imps: T, callSite?: CallSite): UnionToIntersection<T[number]> {
|
||||
const impTypes = reflect(callSite).parameters[0].elements
|
||||
return <any>((...a: any[]) => {
|
||||
for (let i = 0; i < imps.length; ++i) {
|
||||
try {
|
||||
const val = imps[i](...a)
|
||||
return val
|
||||
} catch {
|
||||
const paramTypes = 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
|
||||
}
|
||||
// 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')
|
||||
})
|
||||
}
|
||||
|
@ -1,12 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"moduleResolution": "node",
|
||||
"rootDir": "src",
|
||||
"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": ".",
|
||||
"outDir": "obj"
|
||||
},
|
||||
"include": [
|
||||
"src/steps/three.ts",
|
||||
"node_modules/over.ts/src/index.ts",
|
||||
"src/steps/*.ts",
|
||||
"src/util/*.ts"
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user