From 35a8c62ff2320ef2cc9ebb42c22f7afd37c57c2e Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Wed, 25 Jan 2023 14:42:23 +0100 Subject: [PATCH 01/22] Set up typescript-rtti (WIP) --- .gitignore | 3 +- README.md | 4 +- build/package.json | 3 + package.json5 | 11 ++- pnpm-lock.yaml | 194 +++++++++++++++++++++++++++++++++++++- src/core/Dispatcher.ts | 7 ++ src/index.ts | 52 +++++++++- src/numbers/arithmetic.ts | 2 +- tsconfig.json | 11 ++- 9 files changed, 273 insertions(+), 14 deletions(-) create mode 100644 build/package.json diff --git a/.gitignore b/.gitignore index d0c9d9e..f63a763 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,8 @@ *~ # Typescript # emitted code -obj +build/* +!build/package.json # ---> Node # Logs diff --git a/README.md b/README.md index d15fcc4..49e5266 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,6 @@ A final (?) prototype for a refactor of mathjs, culminating the picomath, pocoma To build and run the prototype, run: ``` -npx tsc -node obj +pnpm install +pnpm build-and-run ``` diff --git a/build/package.json b/build/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/build/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/package.json5 b/package.json5 index 874d5e3..1632098 100644 --- a/package.json5 +++ b/package.json5 @@ -4,6 +4,7 @@ description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept', main: 'index.ts', scripts: { + 'build-and-run': 'ttsc -b && node build', test: 'echo "Error: no test specified" && exit 1', }, keywords: [ @@ -17,7 +18,15 @@ type: 'git', url: 'https://code.studioinfinity.org/glen/typocomath.git', }, + dependencies: { + 'reflect-metadata': '0.1.13', + 'typescript-rtti': '0.8.2', + }, devDependencies: { - typescript: '^4.9.3', + '@types/node': '18.11.18', + 'ts-node': '10.9.1', + ttypescript: '1.5.15', + typescript: '4.8.4', + 'typescript-rtti': '0.8.2', }, } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 095704b..153c7e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,15 +1,199 @@ lockfileVersion: 5.4 specifiers: - typescript: ^4.9.3 + '@types/node': 18.11.18 + reflect-metadata: 0.1.13 + ts-node: 10.9.1 + ttypescript: 1.5.15 + typescript: 4.8.4 + typescript-rtti: 0.8.2 + +dependencies: + reflect-metadata: 0.1.13 + typescript-rtti: 0.8.2_tjkpoysld524rvy7hjviaz6rje devDependencies: - typescript: 4.9.3 + '@types/node': 18.11.18 + ts-node: 10.9.1_vqcafhj4xvr2nzknlrdklk55zm + ttypescript: 1.5.15_mwhvu7sfp6vq5ryuwb6hlbjfka + typescript: 4.8.4 packages: - /typescript/4.9.3: - resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==} - engines: {node: '>=4.2.0'} + /@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.11.18: + resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} + 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.2: + resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} + 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.11.0: + resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} + 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.11.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_vqcafhj4xvr2nzknlrdklk55zm: + 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.11.18 + acorn: 8.8.2 + 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.4 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /ttypescript/1.5.15_mwhvu7sfp6vq5ryuwb6hlbjfka: + resolution: {integrity: sha512-48ykDNHzFnPMnv4hYX1P8Q84TvCZyL1QlFxeuxsuZ48X2+ameBgPenvmCkHJtoOSxpoWTWi8NcgNrRnVDOmfSg==} + hasBin: true + peerDependencies: + ts-node: '>=8.0.2' + typescript: '>=3.2.2' + dependencies: + resolve: 1.22.1 + ts-node: 10.9.1_vqcafhj4xvr2nzknlrdklk55zm + typescript: 4.8.4 + dev: true + + /typescript-rtti/0.8.2_tjkpoysld524rvy7hjviaz6rje: + 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.4 + dev: false + + /typescript/4.8.4: + resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==} + 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 diff --git a/src/core/Dispatcher.ts b/src/core/Dispatcher.ts index def1bd3..1da89ff 100644 --- a/src/core/Dispatcher.ts +++ b/src/core/Dispatcher.ts @@ -1,3 +1,6 @@ +import "reflect-metadata" +import { reflect, type CallSite } from 'typescript-rtti' + /* A Dispatcher is a collection of operations that do run-time * dispatch on the types of their arguments. Thus, every individual * method is like a typed-function (from the library by that name), @@ -46,6 +49,10 @@ export class Dispatcher { // that's really possible, though. ) { console.log('Pretending to install', name, signature, '=>', returns) + + // @ts-ignore + console.log(name, 'signature', reflect(signature)) + console.log(name, 'dependencies', reflect(dependencies)) //TODO: implement me } installType(name: TypeName, typespec: TypeSpecification) { diff --git a/src/index.ts b/src/index.ts index 297b271..5d8c0e7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,57 @@ import {Dispatcher} from './core/Dispatcher.js' import * as Specifications from './all.js' - -export default new Dispatcher(Specifications) - import {Complex} from './Complex/type.js' import {absquare as absquare_complex} from './Complex/arithmetic.js' +import 'reflect-metadata' +import { ReflectedFunctionParameter, ReflectedObjectRef, reflect } from 'typescript-rtti' +import { square } from './generic/arithmetic.js' + +// verify that typescript-rtti works (just as experiment) +const add = (a: number, b: number): number => a + b +console.log('reflect add') +console.log('parameterNames', reflect(add).parameterNames) +console.log('parameterTypes', reflect(add).parameterTypes.map(type => type.toString())) +console.log('returnType', reflect(add).returnType.toString()) +console.log() +// output: +// reflect function add +// parameterNames [ 'a', 'b' ] +// parameterTypes [ 'class Number', 'class Number' ] +// returnType class Number + +// try out a very simple case (just as experiment) +function createSquare(deps: { + multiply: (a: number, b: number) => number, + subtract: (a: number, b: number) => number +}) { + return (a: number) => deps.multiply(a, a) +} +console.log('reflect createSquare') +console.log('parameter names', reflect(createSquare).parameters.map(parameter => parameter.name)) +console.log('parameter[0]', (reflect(createSquare).parameters[0] as ReflectedFunctionParameter)) +// @ts-ignore +console.log('parameterTypes[0]', (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m) +console.log('parameterTypes[0][0]', + // @ts-ignore + (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0].n, + // @ts-ignore + (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0] +) +// @ts-ignore +console.log('parameters[0]', reflect(createSquare).parameters[0]) +// FIXME: where to find the information of the types of the dependencies multiply and subtract? + + + +// FIXME: get all types out of Specifications +// console.log('Specifications', reflect(Specifications)) // Throws errors + + +// TODO: import all specificiations (turned off for debugging purposes) +// export default new Dispatcher(Specifications) + + const mockRealAdd = (a: number, b: number) => a+b const mockComplexAbsquare = (z: Complex) => z.re*z.re + z.im*z.im diff --git a/src/numbers/arithmetic.ts b/src/numbers/arithmetic.ts index 11da5c2..ef0af93 100644 --- a/src/numbers/arithmetic.ts +++ b/src/numbers/arithmetic.ts @@ -10,7 +10,7 @@ export const absquare: Signature<'absquare', number> = a => a * a export const reciprocal: Signature<'reciprocal', number> = a => 1 / a export const divide: Signature<'divide', number> = (a, b) => a / b -const basicSqrt = a => isNaN(a) ? NaN : Math.sqrt(a) +const basicSqrt = (a: number) => isNaN(a) ? NaN : Math.sqrt(a) export const conservativeSqrt: Signature<'conservativeSqrt', number> = basicSqrt export const sqrt = diff --git a/tsconfig.json b/tsconfig.json index aae3a94..4470b45 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,15 @@ "compilerOptions": { "target": "ES2022", "rootDir": "./src", - "outDir": "./obj" + "outDir": "./build", + "esModuleInterop": true, + "allowJs": false, + "noImplicitAny": false, + "moduleResolution": "Node", + "plugins": [ + { + "transform": "typescript-rtti/dist/transformer" + } + ] } } From 1f2a59c8025260a2ed0e3832243a4089e2827dc1 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 2 Feb 2023 15:05:55 +0100 Subject: [PATCH 02/22] fix some exports not being understood by typescript-rtti --- src/Complex/all.ts | 8 ++------ src/numbers/all.ts | 4 +--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Complex/all.ts b/src/Complex/all.ts index 3ff6311..ea13e82 100644 --- a/src/Complex/all.ts +++ b/src/Complex/all.ts @@ -1,6 +1,2 @@ -import * as Complex from './native.js' -import * as complex from './arithmetic.js' - -export { complex } - -export {Complex} +export * as Complex from './native.js' +export * as complex from './arithmetic.js' diff --git a/src/numbers/all.ts b/src/numbers/all.ts index deb4a8e..b71b4c3 100644 --- a/src/numbers/all.ts +++ b/src/numbers/all.ts @@ -1,3 +1 @@ -import * as numbers from './native.js' - -export {numbers} +export * as numbers from './native.js' From 5872bd85377ff4128e33b8f30a316f4e2e10d94a Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 2 Feb 2023 15:07:47 +0100 Subject: [PATCH 03/22] chore: switch to the `typescript` and `typescript-rtti` versions used in the online playground --- package.json5 | 5 ++--- pnpm-lock.yaml | 38 +++++++++++++++++++------------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/package.json5 b/package.json5 index 1632098..68400a2 100644 --- a/package.json5 +++ b/package.json5 @@ -20,13 +20,12 @@ }, dependencies: { 'reflect-metadata': '0.1.13', - 'typescript-rtti': '0.8.2', }, devDependencies: { '@types/node': '18.11.18', 'ts-node': '10.9.1', ttypescript: '1.5.15', - typescript: '4.8.4', - 'typescript-rtti': '0.8.2', + typescript: '4.7.4', + 'typescript-rtti': '0.8.3', }, } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 153c7e0..e7c135f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,18 +5,18 @@ specifiers: reflect-metadata: 0.1.13 ts-node: 10.9.1 ttypescript: 1.5.15 - typescript: 4.8.4 - typescript-rtti: 0.8.2 + typescript: 4.7.4 + typescript-rtti: 0.8.3 dependencies: reflect-metadata: 0.1.13 - typescript-rtti: 0.8.2_tjkpoysld524rvy7hjviaz6rje devDependencies: '@types/node': 18.11.18 - ts-node: 10.9.1_vqcafhj4xvr2nzknlrdklk55zm - ttypescript: 1.5.15_mwhvu7sfp6vq5ryuwb6hlbjfka - typescript: 4.8.4 + ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi + ttypescript: 1.5.15_6oasmw356qmm23djlsjgkwvrtm + typescript: 4.7.4 + typescript-rtti: 0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu packages: @@ -114,7 +114,6 @@ packages: /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==} @@ -130,7 +129,7 @@ packages: engines: {node: '>= 0.4'} dev: true - /ts-node/10.9.1_vqcafhj4xvr2nzknlrdklk55zm: + /ts-node/10.9.1_nv75g3i7xuh23du6z7qul3uiqi: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -156,12 +155,12 @@ packages: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 4.8.4 + typescript: 4.7.4 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 dev: true - /ttypescript/1.5.15_mwhvu7sfp6vq5ryuwb6hlbjfka: + /ttypescript/1.5.15_6oasmw356qmm23djlsjgkwvrtm: resolution: {integrity: sha512-48ykDNHzFnPMnv4hYX1P8Q84TvCZyL1QlFxeuxsuZ48X2+ameBgPenvmCkHJtoOSxpoWTWi8NcgNrRnVDOmfSg==} hasBin: true peerDependencies: @@ -169,25 +168,26 @@ packages: typescript: '>=3.2.2' dependencies: resolve: 1.22.1 - ts-node: 10.9.1_vqcafhj4xvr2nzknlrdklk55zm - typescript: 4.8.4 + ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi + typescript: 4.7.4 dev: true - /typescript-rtti/0.8.2_tjkpoysld524rvy7hjviaz6rje: - resolution: {integrity: sha512-MZUHMX+Up1+7dYaAcOYSTxKc4PNLNXhuXYBkIzEKvQvxJ6xp7wcjTtiIYmB9+RJxRH5/9phZLqKTMpy20aQ4Aw==} + /typescript-rtti/0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu: + resolution: {integrity: sha512-uX1A0JKs1o/ptLJqkubRCGgN7NOCYSTKRXyRIjG80exsLrPDq4jJWMfQxlHMAcv/zjoX0V6iIGU7bwjGWTzpLg==} engines: {node: '>=10'} peerDependencies: reflect-metadata: ^0.1.13 - typescript: '4.6' + typescript: ^4.5 || ^4.6 || ^4.7 dependencies: reflect-metadata: 0.1.13 - typescript: 4.8.4 - dev: false + typescript: 4.7.4 + dev: true - /typescript/4.8.4: - resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==} + /typescript/4.7.4: + resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true + dev: true /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} From 86688ca12935423127b358257b33f723185526d4 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 2 Feb 2023 15:52:15 +0100 Subject: [PATCH 04/22] fix: generate CommonJS output instead of ESM as a workaround for typescript-rtti issue #94 --- .gitignore | 3 +-- build/package.json | 3 --- package.json5 | 2 +- src/index.ts | 43 +++++++++++++++++++++++++++++++++++-------- tsconfig.json | 1 + 5 files changed, 38 insertions(+), 14 deletions(-) delete mode 100644 build/package.json diff --git a/.gitignore b/.gitignore index f63a763..aeea740 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,7 @@ *~ # Typescript # emitted code -build/* -!build/package.json +build # ---> Node # Logs diff --git a/build/package.json b/build/package.json deleted file mode 100644 index 3dbc1ca..0000000 --- a/build/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "module" -} diff --git a/package.json5 b/package.json5 index 68400a2..959a089 100644 --- a/package.json5 +++ b/package.json5 @@ -20,12 +20,12 @@ }, dependencies: { 'reflect-metadata': '0.1.13', + 'typescript-rtti': '0.8.3', }, devDependencies: { '@types/node': '18.11.18', 'ts-node': '10.9.1', ttypescript: '1.5.15', typescript: '4.7.4', - 'typescript-rtti': '0.8.3', }, } diff --git a/src/index.ts b/src/index.ts index 5d8c0e7..13c39fa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,11 @@ +import 'reflect-metadata' + import {Dispatcher} from './core/Dispatcher.js' import * as Specifications from './all.js' import {Complex} from './Complex/type.js' import {absquare as absquare_complex} from './Complex/arithmetic.js' -import 'reflect-metadata' -import { ReflectedFunctionParameter, ReflectedObjectRef, reflect } from 'typescript-rtti' +import { CallSite, ReflectedObjectRef, reflect } from 'typescript-rtti' import { square } from './generic/arithmetic.js' // verify that typescript-rtti works (just as experiment) @@ -21,29 +22,55 @@ console.log() // returnType class Number // try out a very simple case (just as experiment) -function createSquare(deps: { - multiply: (a: number, b: number) => number, - subtract: (a: number, b: number) => number +function createSquare(deps: { + multiply: (a: number, b: number) => number, + subtract: (a: number, b: number) => number }) { return (a: number) => deps.multiply(a, a) } console.log('reflect createSquare') console.log('parameter names', reflect(createSquare).parameters.map(parameter => parameter.name)) -console.log('parameter[0]', (reflect(createSquare).parameters[0] as ReflectedFunctionParameter)) +// console.log('parameter[0]', (reflect(createSquare).parameters[0] as ReflectedFunctionParameter)) // @ts-ignore console.log('parameterTypes[0]', (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m) -console.log('parameterTypes[0][0]', +console.log('parameterTypes[0].ref.m[0]', // @ts-ignore (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0].n, // @ts-ignore (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0] ) +console.log('parameterTypes[0].ref.m[0].t.m', + // @ts-ignore + (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0].t.m +) // @ts-ignore -console.log('parameters[0]', reflect(createSquare).parameters[0]) +// console.log('parameters[0]', reflect(createSquare).parameters[0]) // FIXME: where to find the information of the types of the dependencies multiply and subtract? +// Test whether we loose the type information when casting to a generic interface +// Conclusion: we keep the information, that is good. +console.log() +console.log('reflect createFunction') +type MathjsDependencies = Record +type MathjsCreateFunction = (deps: MathjsDependencies) => Function +const createFunction: MathjsCreateFunction = createSquare as MathjsCreateFunction +console.log('parameter names', reflect(createFunction).parameters.map(parameter => parameter.name)) +// @ts-ignore +console.log('parameterTypes[0]', (reflect(createFunction).parameterTypes[0] as ReflectedObjectRef)._ref.m) + +console.log() +console.log('CallSite') +function foo(call? : CallSite) { + console.log(reflect(call).typeParameters[0].isClass(String)) +} + +function bar(call? : CallSite) { + foo(); +} +bar(); + // FIXME: get all types out of Specifications // console.log('Specifications', reflect(Specifications)) // Throws errors diff --git a/tsconfig.json b/tsconfig.json index 4470b45..c761525 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "allowJs": false, "noImplicitAny": false, "moduleResolution": "Node", + "module": "commonjs", "plugins": [ { "transform": "typescript-rtti/dist/transformer" From 946b4a495f7c64190e7ac763e05df7a2304e583f Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 2 Feb 2023 16:14:25 +0100 Subject: [PATCH 05/22] use `CallSite` reflection to get some information out of `specifications` (WIP) --- src/index.ts | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/index.ts b/src/index.ts index 13c39fa..ce04c21 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import 'reflect-metadata' import {Dispatcher} from './core/Dispatcher.js' -import * as Specifications from './all.js' +import * as specifications from './all.js' import {Complex} from './Complex/type.js' import {absquare as absquare_complex} from './Complex/arithmetic.js' @@ -58,24 +58,29 @@ console.log('parameter names', reflect(createFunction).parameters.map(parameter // @ts-ignore console.log('parameterTypes[0]', (reflect(createFunction).parameterTypes[0] as ReflectedObjectRef)._ref.m) - +// TODO: more specific definition of Specifications +type Specifications = Record> console.log() console.log('CallSite') -function foo(call? : CallSite) { - console.log(reflect(call).typeParameters[0].isClass(String)) +function reflectSpecifications(specifications: Specifications, callSite? : CallSite) { + console.log('specifications', reflect(callSite).parameters[0]) + // @ts-ignore + console.log('specifications', reflect(callSite).parameters[0]._ref) // shows 'numbers', 'Complex, 'complex', 'generic' + // @ts-ignore + console.log('specifications', reflect(callSite).parameters[0]._ref.m + .find(item => item.n === 'generic').t.m) // shows 'square', 'unequal' + // @ts-ignore + console.log('specifications', reflect(callSite).parameters[0]._ref.m + .find(item => item.n === 'generic').t.m + .find(item => item.n === 'square').t.p) // [ { n: 'dep', t: [Function: t], b: undefined, v: null } ] + // @ts-ignore + // FIXME: now, we should be able to get the signature of the multiply dependency of the function square, but how? } - -function bar(call? : CallSite) { - foo(); -} -bar(); - -// FIXME: get all types out of Specifications -// console.log('Specifications', reflect(Specifications)) // Throws errors +reflectSpecifications(specifications); -// TODO: import all specificiations (turned off for debugging purposes) +// TODO: import all specifications (turned off for debugging purposes) // export default new Dispatcher(Specifications) @@ -90,4 +95,5 @@ const quatAbsquare = absquare_complex({ const myabs = quatAbsquare({re: {re: 0, im: 1}, im: {re:2, im: 3}}) const typeTest: typeof myabs = 7 // check myabs is just a number +console.log() console.log('Result is', myabs) From b9cfe706fc04558b32231d84fd0328972548a845 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Mon, 13 Mar 2023 16:04:11 +0100 Subject: [PATCH 06/22] Experiment of creating a TypeScript plugin (WIP) --- package.json5 | 2 + plugins/.gitignore | 1 + plugins/infer.ts | 98 +++++++++++++++++++++++++++++++++ src/generic/arithmeticDirect.ts | 8 +++ src/generic/infer.ts | 4 ++ 5 files changed, 113 insertions(+) create mode 100644 plugins/.gitignore create mode 100644 plugins/infer.ts create mode 100644 src/generic/arithmeticDirect.ts create mode 100644 src/generic/infer.ts diff --git a/package.json5 b/package.json5 index 959a089..c6dd1cb 100644 --- a/package.json5 +++ b/package.json5 @@ -5,6 +5,8 @@ main: 'index.ts', scripts: { 'build-and-run': 'ttsc -b && node build', + 'experiment-infer': 'tsc plugins/infer.ts && node plugins/infer.js ./src/generic/arithmetic.ts', + 'experiment-infer-direct': 'tsc plugins/infer.ts && node plugins/infer.js ./src/generic/arithmeticDirect.ts', test: 'echo "Error: no test specified" && exit 1', }, keywords: [ diff --git a/plugins/.gitignore b/plugins/.gitignore new file mode 100644 index 0000000..a6c7c28 --- /dev/null +++ b/plugins/.gitignore @@ -0,0 +1 @@ +*.js diff --git a/plugins/infer.ts b/plugins/infer.ts new file mode 100644 index 0000000..03b792d --- /dev/null +++ b/plugins/infer.ts @@ -0,0 +1,98 @@ +import { readFileSync } from "fs"; +import * as ts from "typescript"; +import { inspect } from 'util' + +/** + * # The idea + * + * Create a TypeScript plugin which can replace structures like: + * + * infer(factoryFunction) + * + * where `factoryFunction` is a mathjs factory function in TypeScript, with something like: + * + * infer({ signature: factoryFunction }) + * + * where `signature` is a string containing the type of the factory function and its dependencies. + * + * # How to run + * + * pnpm experiment-infer + * pnpm experiment-infer-direct + * + * # Read more + * + * - https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin + * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API + * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Language-Service-API + */ + +export function infer(sourceFile: ts.SourceFile) { + recurse(sourceFile); + + function getType(kind: number) { + switch(kind) { + case ts.SyntaxKind.NumberKeyword: return 'number' + case ts.SyntaxKind.StringKeyword: return 'string' + case ts.SyntaxKind.BooleanKeyword: return 'boolean' + default: return String(ts.SyntaxKind[kind]) // TODO: work out all types + } + } + + function recurse(node: ts.Node) { + if (node.kind === ts.SyntaxKind.Identifier) { + console.log('Identifier', node['escapedText'], ts.SyntaxKind[node.kind]) + } + + // recognize a structure like: + // + // export const square = infer((dep: { + // multiply: (a: number, b: number) => number + // }): (a: number) => number => + // z => dep.multiply(z, z) + // ) + if (node?.['name']?.kind === ts.SyntaxKind.Identifier && node?.['name']['escapedText'] === 'dep') { + console.log('dep', getType(node['type'].kind), node) + + node['type']?.members?.forEach(member => { + console.log('member', { + name: member.name.escapedText, + parameters: member.type.parameters.map(parameter => { + return parameter.name.escapedText + ': ' + getType(parameter.type.kind) + }), + returns: getType(member.type.type.kind) + }) + }) + } + + // recognize a structure like: + // + // export const square = + // (dep: Dependencies<'multiply' | 'unaryMinus', T>): Signature<'square', T> => + // z => dep.multiply(z, z) + if (node?.['name']?.kind === ts.SyntaxKind.Identifier && node?.['name']['escapedText'] === 'dep') { + // TODO + } + + ts.forEachChild(node, recurse); + } +} + +const fileNames = process.argv.slice(2); +console.log('infer files', fileNames) +fileNames.forEach(fileName => { + // Parse a file + const sourceFile = ts.createSourceFile( + fileName, + readFileSync(fileName).toString(), + ts.ScriptTarget.ES2022, + /*setParentNodes */ true + ); + + console.log('AST', fileName, inspect(sourceFile, { depth: null, colors: true })) + + console.log(sourceFile.text) + console.log() + + infer(sourceFile); +}); diff --git a/src/generic/arithmeticDirect.ts b/src/generic/arithmeticDirect.ts new file mode 100644 index 0000000..65a7782 --- /dev/null +++ b/src/generic/arithmeticDirect.ts @@ -0,0 +1,8 @@ +import {infer} from './infer' + +export const square = infer((dep: { + multiply: (a: number, b: number) => number, + unaryMinus: (x: number) => number, // just for the experiment +}): (a: number) => number => + z => dep.multiply(z, z) +) diff --git a/src/generic/infer.ts b/src/generic/infer.ts new file mode 100644 index 0000000..6e654cb --- /dev/null +++ b/src/generic/infer.ts @@ -0,0 +1,4 @@ +export function infer(arg: T) : T { + console.error('infer should be replace with runtime type information by a magic TypeScript plugin') + return arg +} From aa044a54e78f547516fa0f7ef497d91da27c7c3f Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Mon, 13 Mar 2023 16:31:41 +0100 Subject: [PATCH 07/22] Move the experiment into src/plugins and src/experiment --- package.json5 | 4 ++-- plugins/.gitignore | 1 - .../arithmeticDirect.ts => experiment/arithmeticInfer.ts} | 2 +- {plugins => src/plugins}/infer.ts | 1 + 4 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 plugins/.gitignore rename src/{generic/arithmeticDirect.ts => experiment/arithmeticInfer.ts} (84%) rename {plugins => src/plugins}/infer.ts (97%) diff --git a/package.json5 b/package.json5 index c6dd1cb..2fcf926 100644 --- a/package.json5 +++ b/package.json5 @@ -5,8 +5,8 @@ main: 'index.ts', scripts: { 'build-and-run': 'ttsc -b && node build', - 'experiment-infer': 'tsc plugins/infer.ts && node plugins/infer.js ./src/generic/arithmetic.ts', - 'experiment-infer-direct': 'tsc plugins/infer.ts && node plugins/infer.js ./src/generic/arithmeticDirect.ts', + 'experiment-infer': 'ttsc -b && node build/plugins/infer.js ./src/generic/arithmetic.ts', + 'experiment-infer-direct': 'ttsc -b && node build/plugins/infer.js ./src/experiment/arithmeticInfer.ts', test: 'echo "Error: no test specified" && exit 1', }, keywords: [ diff --git a/plugins/.gitignore b/plugins/.gitignore deleted file mode 100644 index a6c7c28..0000000 --- a/plugins/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.js diff --git a/src/generic/arithmeticDirect.ts b/src/experiment/arithmeticInfer.ts similarity index 84% rename from src/generic/arithmeticDirect.ts rename to src/experiment/arithmeticInfer.ts index 65a7782..92aeffd 100644 --- a/src/generic/arithmeticDirect.ts +++ b/src/experiment/arithmeticInfer.ts @@ -1,4 +1,4 @@ -import {infer} from './infer' +import {infer} from '../generic/infer' export const square = infer((dep: { multiply: (a: number, b: number) => number, diff --git a/plugins/infer.ts b/src/plugins/infer.ts similarity index 97% rename from plugins/infer.ts rename to src/plugins/infer.ts index 03b792d..8efdc22 100644 --- a/plugins/infer.ts +++ b/src/plugins/infer.ts @@ -24,6 +24,7 @@ import { inspect } from 'util' * * - https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API + * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Language-Service-API */ From f8553aa748fff0e843661fe9b71a09583a035aac Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Tue, 14 Mar 2023 09:51:28 +0100 Subject: [PATCH 08/22] Add another experiment infer2 (WIP) --- README.md | 50 +++++++++++++ package.json5 | 5 +- ...arithmeticInfer.ts => arithmeticInfer1.ts} | 0 src/experiment/arithmeticInfer2.ts | 15 ++++ src/plugins/{infer.ts => infer1.ts} | 26 ------- src/plugins/infer2.ts | 74 +++++++++++++++++++ 6 files changed, 142 insertions(+), 28 deletions(-) rename src/experiment/{arithmeticInfer.ts => arithmeticInfer1.ts} (100%) create mode 100644 src/experiment/arithmeticInfer2.ts rename src/plugins/{infer.ts => infer1.ts} (75%) create mode 100644 src/plugins/infer2.ts diff --git a/README.md b/README.md index 49e5266..44b4a46 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,53 @@ To build and run the prototype, run: pnpm install pnpm build-and-run ``` + +## experiment + +See: the section under `/src/experiment` and `/src/plugins`. + +### The idea + +Create a TypeScript plugin which can replace structures like: + + infer(factoryFunction) + +where `factoryFunction` is a mathjs factory function in TypeScript, with something like: + + infer({ signature: factoryFunction }) + +where `signature` is a string containing the type of the factory function and its dependencies. + +Relevant methods of the TypeScript compiler are: + +```ts +const program = ts.createProgram(fileNames, options) +const typeChecker = program.getTypeChecker() + +// relevant methods: +// +// typeChecker.getSymbolAtLocation +// typeChecker.getTypeOfSymbolAtLocation +// typeChecker.getResolvedSignature +// typeChecker.getSignaturesOfType +``` + +### Status + +None of the experiments (`infer1` and `infer2`) are outputting something useful yet. + + +### How to run + + pnpm experiment:infer1 + pnpm experiment:infer1-direct + pnpm experiment:infer2 + +### Read more + +- https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin +- https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API +- https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker +- https://github.com/Microsoft/TypeScript/wiki/Using-the-Language-Service-API +- https://stackoverflow.com/questions/63944135/typescript-compiler-api-how-to-get-type-with-resolved-type-arguments +- https://stackoverflow.com/questions/48886508/typechecker-api-how-do-i-find-inferred-type-arguments-to-a-function \ No newline at end of file diff --git a/package.json5 b/package.json5 index 2fcf926..a140cb8 100644 --- a/package.json5 +++ b/package.json5 @@ -5,8 +5,9 @@ main: 'index.ts', scripts: { 'build-and-run': 'ttsc -b && node build', - 'experiment-infer': 'ttsc -b && node build/plugins/infer.js ./src/generic/arithmetic.ts', - 'experiment-infer-direct': 'ttsc -b && node build/plugins/infer.js ./src/experiment/arithmeticInfer.ts', + 'experiment:infer1': 'ttsc -b && node build/plugins/infer.js ./src/generic/arithmetic.ts', + 'experiment:infer1-direct': 'ttsc -b && node build/plugins/infer.js ./src/experiment/arithmeticInfer1.ts', + 'experiment:infer2': 'ttsc -b && node build/plugins/infer2.js ./src/experiment/arithmeticInfer2.ts', test: 'echo "Error: no test specified" && exit 1', }, keywords: [ diff --git a/src/experiment/arithmeticInfer.ts b/src/experiment/arithmeticInfer1.ts similarity index 100% rename from src/experiment/arithmeticInfer.ts rename to src/experiment/arithmeticInfer1.ts diff --git a/src/experiment/arithmeticInfer2.ts b/src/experiment/arithmeticInfer2.ts new file mode 100644 index 0000000..ab4eebf --- /dev/null +++ b/src/experiment/arithmeticInfer2.ts @@ -0,0 +1,15 @@ +import { infer } from '../generic/infer' +import { Dependencies, Signature } from '../interfaces/type' + +export type multiplyDep = Dependencies<'multiply', T> + +export const square1 = + (dep: Dependencies<'multiply', T>): Signature<'square', T> => + z => dep.multiply(z, z) + +export const square2 = infer((dep: { + multiply: (a: number, b: number) => number, + unaryMinus: (x: number) => number, // just for the experiment +}): (a: number) => number => + z => dep.multiply(z, z) +) diff --git a/src/plugins/infer.ts b/src/plugins/infer1.ts similarity index 75% rename from src/plugins/infer.ts rename to src/plugins/infer1.ts index 8efdc22..81fa0a6 100644 --- a/src/plugins/infer.ts +++ b/src/plugins/infer1.ts @@ -2,32 +2,6 @@ import { readFileSync } from "fs"; import * as ts from "typescript"; import { inspect } from 'util' -/** - * # The idea - * - * Create a TypeScript plugin which can replace structures like: - * - * infer(factoryFunction) - * - * where `factoryFunction` is a mathjs factory function in TypeScript, with something like: - * - * infer({ signature: factoryFunction }) - * - * where `signature` is a string containing the type of the factory function and its dependencies. - * - * # How to run - * - * pnpm experiment-infer - * pnpm experiment-infer-direct - * - * # Read more - * - * - https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin - * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API - * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker - * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Language-Service-API - */ - export function infer(sourceFile: ts.SourceFile) { recurse(sourceFile); diff --git a/src/plugins/infer2.ts b/src/plugins/infer2.ts new file mode 100644 index 0000000..43fc64b --- /dev/null +++ b/src/plugins/infer2.ts @@ -0,0 +1,74 @@ +import * as ts from "typescript" + +// based on: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker + +infer2(process.argv.slice(2), { + target: ts.ScriptTarget.ES5, + module: ts.ModuleKind.CommonJS +}) + +function infer2( + fileNames: string[], + options: ts.CompilerOptions +): void { + const program = ts.createProgram(fileNames, options) + const typeChecker = program.getTypeChecker() + + for (const sourceFile of program.getSourceFiles()) { + if (!sourceFile.isDeclarationFile) { + ts.forEachChild(sourceFile, visit) + } + } + + return + + function visit(node: ts.Node) { + // // Only consider exported nodes + // if (!isNodeExported(node)) { + // return; + // } + + // console.log('Node', node.kind, node?.['name']?.escapedText) + + if (ts.isModuleDeclaration(node)) { + // This is a namespace, visit its children + console.log('check') + ts.forEachChild(node, visit); + } else if (ts.isTypeAliasDeclaration(node)) { + console.log('isTypeAliasDeclaration', node.name.escapedText) + + let symbol = typeChecker.getSymbolAtLocation(node.name); + if (symbol) { + const symbolType = typeChecker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration) + const symbolSignature = typeChecker.getSignaturesOfType(symbolType, ts.SignatureKind.Call) + + // checker.getResolvedSignature(symbol) + console.log('symbol', symbol.getName(), symbolSignature) + + // getTypeOfSymbolAtLocation + // getResolvedSignature + } + } else if (ts.isCallExpression(node)) { + console.log('isCallExpression', node.expression) + } else if (ts.isFunctionDeclaration(node)) { + console.log('isFunctionDeclaration', node.name.escapedText, { typeParameter0: node.typeParameters[0] }) + + if (node.name.escapedText === 'infer') { + const param0 = node.typeParameters[0] + if (ts.isPropertyDeclaration(param0)) { + const symbol = typeChecker.getSymbolAtLocation(param0) + + // TODO: get resolving + + // console.log('getResolvedSignature', typeChecker.getResolvedSignature(node) ) + + // const symbolType = typeChecker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration) + // const symbolSignature = typeChecker.getSignaturesOfType(symbolType, ts.SignatureKind.Call) + // console.log('symbol', symbol.getName(), symbolSignature) + + // console.log('getSignaturesOfType', typeChecker.getSignaturesOfType(param0) + } + } + } + } +} From 6a063d738540c72db3ea098af8511e4010447f4e Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Tue, 14 Mar 2023 10:15:56 +0100 Subject: [PATCH 09/22] Add a useful resource to the list with articles --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 44b4a46..ebd41b6 100644 --- a/README.md +++ b/README.md @@ -57,4 +57,5 @@ None of the experiments (`infer1` and `infer2`) are outputting something useful - https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker - https://github.com/Microsoft/TypeScript/wiki/Using-the-Language-Service-API - https://stackoverflow.com/questions/63944135/typescript-compiler-api-how-to-get-type-with-resolved-type-arguments -- https://stackoverflow.com/questions/48886508/typechecker-api-how-do-i-find-inferred-type-arguments-to-a-function \ No newline at end of file +- https://stackoverflow.com/questions/48886508/typechecker-api-how-do-i-find-inferred-type-arguments-to-a-function +- https://blog.logrocket.com/using-typescript-transforms-to-enrich-runtime-code-3fd2863221ed/ From 16eb09fe6107bb1e352dbad5b40d9ab6fa7fff15 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Thu, 17 Aug 2023 23:34:03 -0700 Subject: [PATCH 10/22] chore: update a couple packages --- package.json5 | 1 + pnpm-lock.yaml | 106 ++++++++++++++++++++++++++++--------------------- 2 files changed, 62 insertions(+), 45 deletions(-) diff --git a/package.json5 b/package.json5 index a140cb8..0044401 100644 --- a/package.json5 +++ b/package.json5 @@ -23,6 +23,7 @@ }, dependencies: { 'reflect-metadata': '0.1.13', + 'source-map': '^0.7.4', 'typescript-rtti': '0.8.3', }, devDependencies: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e7c135f..8ec9e4e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,121 +1,133 @@ -lockfileVersion: 5.4 +lockfileVersion: '6.0' -specifiers: - '@types/node': 18.11.18 - reflect-metadata: 0.1.13 - ts-node: 10.9.1 - ttypescript: 1.5.15 - typescript: 4.7.4 - typescript-rtti: 0.8.3 +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false dependencies: - reflect-metadata: 0.1.13 + reflect-metadata: + specifier: 0.1.13 + version: 0.1.13 + source-map: + specifier: ^0.7.4 + version: 0.7.4 + typescript-rtti: + specifier: 0.8.3 + version: 0.8.3(reflect-metadata@0.1.13)(typescript@4.7.4) devDependencies: - '@types/node': 18.11.18 - ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi - ttypescript: 1.5.15_6oasmw356qmm23djlsjgkwvrtm - typescript: 4.7.4 - typescript-rtti: 0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu + '@types/node': + specifier: 18.11.18 + version: 18.11.18 + ts-node: + specifier: 10.9.1 + version: 10.9.1(@types/node@18.11.18)(typescript@4.7.4) + ttypescript: + specifier: 1.5.15 + version: 1.5.15(ts-node@10.9.1)(typescript@4.7.4) + typescript: + specifier: 4.7.4 + version: 4.7.4 packages: - /@cspotcode/source-map-support/0.8.1: + /@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: + /@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: + /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} dev: true - /@jridgewell/trace-mapping/0.3.9: + /@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: + /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true - /@tsconfig/node12/1.0.11: + /@tsconfig/node12@1.0.11: resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} dev: true - /@tsconfig/node14/1.0.3: + /@tsconfig/node14@1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} dev: true - /@tsconfig/node16/1.0.3: + /@tsconfig/node16@1.0.3: resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} dev: true - /@types/node/18.11.18: + /@types/node@18.11.18: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} dev: true - /acorn-walk/8.2.0: + /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} dev: true - /acorn/8.8.2: + /acorn@8.8.2: resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} engines: {node: '>=0.4.0'} hasBin: true dev: true - /arg/4.1.3: + /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true - /create-require/1.1.1: + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true - /diff/4.0.2: + /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} dev: true - /function-bind/1.1.1: + /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true - /has/1.0.3: + /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.11.0: + /is-core-module@2.11.0: resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: has: 1.0.3 dev: true - /make-error/1.3.6: + /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /path-parse/1.0.7: + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true - /reflect-metadata/0.1.13: + /reflect-metadata@0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + dev: false - /resolve/1.22.1: + /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true dependencies: @@ -124,12 +136,17 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true - /supports-preserve-symlinks-flag/1.0.0: + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: false + + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /ts-node/10.9.1_nv75g3i7xuh23du6z7qul3uiqi: + /ts-node@10.9.1(@types/node@18.11.18)(typescript@4.7.4): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -160,7 +177,7 @@ packages: yn: 3.1.1 dev: true - /ttypescript/1.5.15_6oasmw356qmm23djlsjgkwvrtm: + /ttypescript@1.5.15(ts-node@10.9.1)(typescript@4.7.4): resolution: {integrity: sha512-48ykDNHzFnPMnv4hYX1P8Q84TvCZyL1QlFxeuxsuZ48X2+ameBgPenvmCkHJtoOSxpoWTWi8NcgNrRnVDOmfSg==} hasBin: true peerDependencies: @@ -168,11 +185,11 @@ packages: typescript: '>=3.2.2' dependencies: resolve: 1.22.1 - ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi + ts-node: 10.9.1(@types/node@18.11.18)(typescript@4.7.4) typescript: 4.7.4 dev: true - /typescript-rtti/0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu: + /typescript-rtti@0.8.3(reflect-metadata@0.1.13)(typescript@4.7.4): resolution: {integrity: sha512-uX1A0JKs1o/ptLJqkubRCGgN7NOCYSTKRXyRIjG80exsLrPDq4jJWMfQxlHMAcv/zjoX0V6iIGU7bwjGWTzpLg==} engines: {node: '>=10'} peerDependencies: @@ -181,19 +198,18 @@ packages: dependencies: reflect-metadata: 0.1.13 typescript: 4.7.4 - dev: true + dev: false - /typescript/4.7.4: + /typescript@4.7.4: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true - dev: true - /v8-compile-cache-lib/3.0.1: + /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true - /yn/3.1.1: + /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} dev: true From 3653077c9507ca1ac0b552a931333eff5ad353fb Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 18 Aug 2023 17:39:57 +0200 Subject: [PATCH 11/22] Add an experiment reading out a JSDoc comment --- README.md | 2 + package.json5 | 5 +- pnpm-lock.yaml | 125 +++++++++++++---------------- src/experiment/arithmeticInfer3.js | 16 ++++ src/generic/infer.ts | 2 +- src/plugins/infer1.ts | 2 +- src/plugins/infer3.ts | 67 ++++++++++++++++ 7 files changed, 148 insertions(+), 71 deletions(-) create mode 100644 src/experiment/arithmeticInfer3.js create mode 100644 src/plugins/infer3.ts diff --git a/README.md b/README.md index ebd41b6..6402234 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ None of the experiments (`infer1` and `infer2`) are outputting something useful pnpm experiment:infer1 pnpm experiment:infer1-direct pnpm experiment:infer2 + pnpm experiment:infer3 ### Read more @@ -59,3 +60,4 @@ None of the experiments (`infer1` and `infer2`) are outputting something useful - https://stackoverflow.com/questions/63944135/typescript-compiler-api-how-to-get-type-with-resolved-type-arguments - https://stackoverflow.com/questions/48886508/typechecker-api-how-do-i-find-inferred-type-arguments-to-a-function - https://blog.logrocket.com/using-typescript-transforms-to-enrich-runtime-code-3fd2863221ed/ +- https://github.com/itsdouges/typescript-transformer-handbook#transforms diff --git a/package.json5 b/package.json5 index 0044401..3c214e5 100644 --- a/package.json5 +++ b/package.json5 @@ -5,9 +5,10 @@ main: 'index.ts', scripts: { 'build-and-run': 'ttsc -b && node build', - 'experiment:infer1': 'ttsc -b && node build/plugins/infer.js ./src/generic/arithmetic.ts', - 'experiment:infer1-direct': 'ttsc -b && node build/plugins/infer.js ./src/experiment/arithmeticInfer1.ts', + 'experiment:infer1': 'ttsc -b && node build/plugins/infer1.js ./src/generic/arithmetic.ts', + 'experiment:infer1-direct': 'ttsc -b && node build/plugins/infer1.js ./src/experiment/arithmeticInfer1.ts', 'experiment:infer2': 'ttsc -b && node build/plugins/infer2.js ./src/experiment/arithmeticInfer2.ts', + 'experiment:infer3': 'ttsc -b && node build/plugins/infer3.js ./src/experiment/arithmeticInfer3.js', test: 'echo "Error: no test specified" && exit 1', }, keywords: [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8ec9e4e..47c4737 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,152 +1,143 @@ -lockfileVersion: '6.0' +lockfileVersion: 5.4 -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false +specifiers: + '@types/node': 18.11.18 + reflect-metadata: 0.1.13 + source-map: ^0.7.4 + ts-node: 10.9.1 + ttypescript: 1.5.15 + typescript: 4.7.4 + typescript-rtti: 0.8.3 dependencies: - reflect-metadata: - specifier: 0.1.13 - version: 0.1.13 - source-map: - specifier: ^0.7.4 - version: 0.7.4 - typescript-rtti: - specifier: 0.8.3 - version: 0.8.3(reflect-metadata@0.1.13)(typescript@4.7.4) + reflect-metadata: 0.1.13 + source-map: 0.7.4 + typescript-rtti: 0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu devDependencies: - '@types/node': - specifier: 18.11.18 - version: 18.11.18 - ts-node: - specifier: 10.9.1 - version: 10.9.1(@types/node@18.11.18)(typescript@4.7.4) - ttypescript: - specifier: 1.5.15 - version: 1.5.15(ts-node@10.9.1)(typescript@4.7.4) - typescript: - specifier: 4.7.4 - version: 4.7.4 + '@types/node': 18.11.18 + ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi + ttypescript: 1.5.15_6oasmw356qmm23djlsjgkwvrtm + typescript: 4.7.4 packages: - /@cspotcode/source-map-support@0.8.1: + /@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==} + /@jridgewell/resolve-uri/3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/sourcemap-codec@1.4.14: - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + /@jridgewell/sourcemap-codec/1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} dev: true - /@jridgewell/trace-mapping@0.3.9: + /@jridgewell/trace-mapping/0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /@tsconfig/node10@1.0.9: + /@tsconfig/node10/1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true - /@tsconfig/node12@1.0.11: + /@tsconfig/node12/1.0.11: resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} dev: true - /@tsconfig/node14@1.0.3: + /@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==} + /@tsconfig/node16/1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true - /@types/node@18.11.18: + /@types/node/18.11.18: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} dev: true - /acorn-walk@8.2.0: + /acorn-walk/8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} dev: true - /acorn@8.8.2: - resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} + /acorn/8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} engines: {node: '>=0.4.0'} hasBin: true dev: true - /arg@4.1.3: + /arg/4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true - /create-require@1.1.1: + /create-require/1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true - /diff@4.0.2: + /diff/4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} dev: true - /function-bind@1.1.1: + /function-bind/1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true - /has@1.0.3: + /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.11.0: - resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} + /is-core-module/2.13.0: + resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} dependencies: has: 1.0.3 dev: true - /make-error@1.3.6: + /make-error/1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /path-parse@1.0.7: + /path-parse/1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true - /reflect-metadata@0.1.13: + /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==} + /resolve/1.22.4: + resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} hasBin: true dependencies: - is-core-module: 2.11.0 + is-core-module: 2.13.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 dev: true - /source-map@0.7.4: + /source-map/0.7.4: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} engines: {node: '>= 8'} dev: false - /supports-preserve-symlinks-flag@1.0.0: + /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /ts-node@10.9.1(@types/node@18.11.18)(typescript@4.7.4): + /ts-node/10.9.1_nv75g3i7xuh23du6z7qul3uiqi: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -164,9 +155,9 @@ packages: '@tsconfig/node10': 1.0.9 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.3 + '@tsconfig/node16': 1.0.4 '@types/node': 18.11.18 - acorn: 8.8.2 + acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 create-require: 1.1.1 @@ -177,19 +168,19 @@ packages: yn: 3.1.1 dev: true - /ttypescript@1.5.15(ts-node@10.9.1)(typescript@4.7.4): + /ttypescript/1.5.15_6oasmw356qmm23djlsjgkwvrtm: resolution: {integrity: sha512-48ykDNHzFnPMnv4hYX1P8Q84TvCZyL1QlFxeuxsuZ48X2+ameBgPenvmCkHJtoOSxpoWTWi8NcgNrRnVDOmfSg==} hasBin: true peerDependencies: ts-node: '>=8.0.2' typescript: '>=3.2.2' dependencies: - resolve: 1.22.1 - ts-node: 10.9.1(@types/node@18.11.18)(typescript@4.7.4) + resolve: 1.22.4 + ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi typescript: 4.7.4 dev: true - /typescript-rtti@0.8.3(reflect-metadata@0.1.13)(typescript@4.7.4): + /typescript-rtti/0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu: resolution: {integrity: sha512-uX1A0JKs1o/ptLJqkubRCGgN7NOCYSTKRXyRIjG80exsLrPDq4jJWMfQxlHMAcv/zjoX0V6iIGU7bwjGWTzpLg==} engines: {node: '>=10'} peerDependencies: @@ -200,16 +191,16 @@ packages: typescript: 4.7.4 dev: false - /typescript@4.7.4: + /typescript/4.7.4: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true - /v8-compile-cache-lib@3.0.1: + /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true - /yn@3.1.1: + /yn/3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} dev: true diff --git a/src/experiment/arithmeticInfer3.js b/src/experiment/arithmeticInfer3.js new file mode 100644 index 0000000..2d0b6da --- /dev/null +++ b/src/experiment/arithmeticInfer3.js @@ -0,0 +1,16 @@ +// @ts-check + +/** + * Function square + * + * Description of function square bla bla bla + * + * @param {{ + * multiply: (a: number, b: number) => number, + * unaryMinus: (x: number) => number + * }} dep + * @return {(a: number) => number} + */ +export function square3 (dep) { + return z => dep.multiply(z, z) +} diff --git a/src/generic/infer.ts b/src/generic/infer.ts index 6e654cb..ac038dd 100644 --- a/src/generic/infer.ts +++ b/src/generic/infer.ts @@ -1,4 +1,4 @@ export function infer(arg: T) : T { - console.error('infer should be replace with runtime type information by a magic TypeScript plugin') + console.error('infer should be replaced with runtime type information by a magic TypeScript plugin') return arg } diff --git a/src/plugins/infer1.ts b/src/plugins/infer1.ts index 81fa0a6..9aea0da 100644 --- a/src/plugins/infer1.ts +++ b/src/plugins/infer1.ts @@ -27,7 +27,7 @@ export function infer(sourceFile: ts.SourceFile) { // z => dep.multiply(z, z) // ) if (node?.['name']?.kind === ts.SyntaxKind.Identifier && node?.['name']['escapedText'] === 'dep') { - console.log('dep', getType(node['type'].kind), node) + // console.log('dep', getType(node['type'].kind), node) node['type']?.members?.forEach(member => { console.log('member', { diff --git a/src/plugins/infer3.ts b/src/plugins/infer3.ts new file mode 100644 index 0000000..c57fd22 --- /dev/null +++ b/src/plugins/infer3.ts @@ -0,0 +1,67 @@ +import { readFileSync } from "fs"; +import * as ts from "typescript"; +import { inspect } from 'util' + +export function infer3(sourceFile: ts.SourceFile) { + recurse(sourceFile); + + function getType(kind: number) { + switch(kind) { + case ts.SyntaxKind.NumberKeyword: return 'number' + case ts.SyntaxKind.StringKeyword: return 'string' + case ts.SyntaxKind.BooleanKeyword: return 'boolean' + case ts.SyntaxKind.JSDoc: return 'jsdoc' + default: return String(ts.SyntaxKind[kind]) // TODO: work out all types + } + } + + function recurse(node: ts.Node) { + if (node.kind === ts.SyntaxKind.Identifier) { + console.log('Identifier', node['escapedText'], ts.SyntaxKind[node.kind]) + } + + if (node['jsDoc']) { + console.log('Found a JSDoc comment:') + // console.log(inspect(node['jsDoc'], { depth: null, colors: true })) + + const fullComment = sourceFile.text.slice(node.pos, node.end) + console.log(fullComment) + // TODO: next steps: + // - either get the types from the TypeScript AST, + // or extract them ourselves with regex or anything from the comment text + // - After that, we have to transform the source file and insert the comments + // as string or object that is runtime accessible in JavaScript + } + + ts.forEachChild(node, recurse); + } +} + +const fileNames = process.argv.slice(2); +console.log('infer files', fileNames) +fileNames.forEach(fileName => { + // Parse a file + const sourceFile = ts.createSourceFile( + fileName, + readFileSync(fileName).toString(), + ts.ScriptTarget.ES2022, + /*setParentNodes */ true + ); + + console.log('FILE') + console.log(fileName) + console.log() + + console.log('SOURCE') + console.log(sourceFile.text) + console.log() + + console.log('AST') + console.log(inspect(sourceFile, { depth: null, colors: true })) + console.log() + + console.log('INFER') + infer3(sourceFile); +}); + +console.log('test!!!') From 2cb8bc0099d151379e88ec2592ff68044453a609 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 18 Aug 2023 17:40:13 +0200 Subject: [PATCH 12/22] Cleanup a temporary console.log --- src/plugins/infer3.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/infer3.ts b/src/plugins/infer3.ts index c57fd22..5c599db 100644 --- a/src/plugins/infer3.ts +++ b/src/plugins/infer3.ts @@ -63,5 +63,3 @@ fileNames.forEach(fileName => { console.log('INFER') infer3(sourceFile); }); - -console.log('test!!!') From dea521029e821da7a11a82bbb7585a7162e55dc1 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 1 Sep 2023 17:52:44 +0200 Subject: [PATCH 13/22] Get a real TypeScript plugin working --- README.md | 6 +++ package.json5 | 2 + src/experiment/arithmeticInfer4.ts | 8 ++++ src/generic/infer.ts | 5 ++ src/plugins/infer4.ts | 75 ++++++++++++++++++++++++++++++ src/plugins/myFirstPlugin.ts | 58 +++++++++++++++++++++++ tsconfig.json | 9 ++-- 7 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 src/experiment/arithmeticInfer4.ts create mode 100644 src/plugins/infer4.ts create mode 100644 src/plugins/myFirstPlugin.ts diff --git a/README.md b/README.md index 6402234..897dd0b 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ None of the experiments (`infer1` and `infer2`) are outputting something useful pnpm experiment:infer1-direct pnpm experiment:infer2 pnpm experiment:infer3 + pnpm experiment:infer4 ### Read more @@ -61,3 +62,8 @@ None of the experiments (`infer1` and `infer2`) are outputting something useful - https://stackoverflow.com/questions/48886508/typechecker-api-how-do-i-find-inferred-type-arguments-to-a-function - https://blog.logrocket.com/using-typescript-transforms-to-enrich-runtime-code-3fd2863221ed/ - https://github.com/itsdouges/typescript-transformer-handbook#transforms + +### Interesting libraries + +- https://github.com/GoogleFeud/ts-macros/ +- https://ts-morph.com diff --git a/package.json5 b/package.json5 index 3c214e5..1f3d6e0 100644 --- a/package.json5 +++ b/package.json5 @@ -9,6 +9,8 @@ 'experiment:infer1-direct': 'ttsc -b && node build/plugins/infer1.js ./src/experiment/arithmeticInfer1.ts', 'experiment:infer2': 'ttsc -b && node build/plugins/infer2.js ./src/experiment/arithmeticInfer2.ts', 'experiment:infer3': 'ttsc -b && node build/plugins/infer3.js ./src/experiment/arithmeticInfer3.js', + 'experiment:infer4': 'ttsc -b && node build/plugins/infer4.js ./src/experiment/arithmeticInfer4.ts', + 'experiment:infer4:plugin': 'ttsc -b', test: 'echo "Error: no test specified" && exit 1', }, keywords: [ diff --git a/src/experiment/arithmeticInfer4.ts b/src/experiment/arithmeticInfer4.ts new file mode 100644 index 0000000..dc2f45a --- /dev/null +++ b/src/experiment/arithmeticInfer4.ts @@ -0,0 +1,8 @@ +import { typed } from '../generic/infer' + +export const square = typed('__infer__', (dep: { + multiply: (a: T, b: T) => T, + unaryMinus: (x: T) => T, // just for the experiment +}): (a: T) => T => + z => dep.multiply(z, z) +) diff --git a/src/generic/infer.ts b/src/generic/infer.ts index ac038dd..92db72d 100644 --- a/src/generic/infer.ts +++ b/src/generic/infer.ts @@ -2,3 +2,8 @@ export function infer(arg: T) : T { console.error('infer should be replaced with runtime type information by a magic TypeScript plugin') return arg } + +export function typed(dep: string, arg: T) : T { + console.error('infer should be replaced with runtime type information by a magic TypeScript plugin') + return arg +} diff --git a/src/plugins/infer4.ts b/src/plugins/infer4.ts new file mode 100644 index 0000000..c5e7834 --- /dev/null +++ b/src/plugins/infer4.ts @@ -0,0 +1,75 @@ +import { readFileSync } from "fs"; +import * as ts from "typescript"; +import { inspect } from 'util' + +export function infer(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) { + recurse(sourceFile); + + function recurse(node: ts.Node) { + if (ts.isCallExpression(node)) { + if (ts.isIdentifier(node.expression) && node.expression.escapedText === 'infer') { + const argNode = node.arguments[0] + + if (ts.isArrowFunction(argNode)) { + const text = sourceFile.text.slice(argNode.pos, argNode.end) + console.log('infer', text) + + console.log('AST') + console.log(node) + console.log() + + const returnType = argNode.type.getText(sourceFile) + console.log('returnType', returnType) + // (a: number) => number + + const paramNode = argNode.parameters[0] + + const paramType = paramNode.type.getText(sourceFile) + console.log('paramType', paramType) // (a: number) => number + // { + // multiply: (a: number, b: number) => number, + // unaryMinus: (x: number) => number, // just for the experiment + // } + + const type = typeChecker.getTypeAtLocation(paramNode) + const typeStr = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) + console.log('paramTypeString', typeStr) + // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } + } + + } + } + + ts.forEachChild(node, recurse); + } +} + +const fileNames = process.argv.slice(2); +console.log('infer files', fileNames) + +const options = { + target: ts.ScriptTarget.ES2022, + module: ts.ModuleKind.ES2022 +} +let program = ts.createProgram(fileNames, options); +const checker = program.getTypeChecker() + +for (const sourceFile of program.getSourceFiles()) { + if (!sourceFile.isDeclarationFile) { + console.log('FILE') + console.log(sourceFile.fileName) + console.log() + + console.log('SOURCE') + console.log(sourceFile.text) + console.log() + + // console.log('AST') + // console.log(inspect(sourceFile, { depth: null, colors: true })) + // console.log() + + console.log('INFER') + infer(sourceFile, checker); + + } +} diff --git a/src/plugins/myFirstPlugin.ts b/src/plugins/myFirstPlugin.ts new file mode 100644 index 0000000..ba2feb6 --- /dev/null +++ b/src/plugins/myFirstPlugin.ts @@ -0,0 +1,58 @@ +import * as ts from 'typescript'; + +const transformer: ts.TransformerFactory = context => { + // TODO: get a reference to the program instance that the plugin is running in instead of creating a new program? + const program = ts.createProgram([], {}) + const checker = program.getTypeChecker() + + return sourceFile => { + // For the experiment we only want to influence a single file + if (!sourceFile.fileName.endsWith('arithmeticInfer4.ts')) { + return sourceFile + } + + const visitor = (node: ts.Node): ts.Node => { + // @ts-ignore + if (ts.isStringLiteral(node) && node.text === '__infer__') { + console.log('STRING LITERAL', node.text) + console.log(node) + + const parentNode = node.parent + console.log('PARENT') + console.log(parentNode) + + // TODO: validate that the parent is indeed a mathjs typed function with deps + + // @ts-ignore + const argNode = parentNode.arguments[1] + const returnType = argNode.type.getText(sourceFile) + console.log('RETURN TYPE') + console.log(returnType) + // (a: number) => number + + const paramNode = argNode.parameters[0] + const paramTypeSrc = paramNode.type.getText(sourceFile) + console.log('PARAM TYPE SRC', paramTypeSrc) + // { + // multiply: (a: number, b: number) => number, + // unaryMinus: (x: number) => number, // just for the experiment + // } + + const type = checker.getTypeAtLocation(paramNode) + const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) + console.log('PARAM TYPE STRING', paramType) + // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } + + const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` + + return ts.factory.createStringLiteral(depsAndReturnType) + } + + return ts.visitEachChild(node, visitor, context); + }; + + return ts.visitNode(sourceFile, visitor); + }; +}; + +export default transformer; diff --git a/tsconfig.json b/tsconfig.json index c761525..50df804 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,10 +8,9 @@ "noImplicitAny": false, "moduleResolution": "Node", "module": "commonjs", - "plugins": [ - { - "transform": "typescript-rtti/dist/transformer" - } - ] + "plugins": [{ + "transform": "./src/plugins/myFirstPlugin.ts", + "type": "raw" + }] } } From cbb79d46fe07a5fd038423e890d0abba8b0bbac1 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 1 Sep 2023 18:21:45 +0200 Subject: [PATCH 14/22] cleanup old experiments and typescript-rtti, update readme --- README.md | 42 ++----- package.json5 | 14 +-- pnpm-lock.yaml | 44 ++----- src/core/Dispatcher.ts | 9 +- ...arithmeticInfer4.ts => arithmeticInfer.ts} | 2 +- src/experiment/arithmeticInfer1.ts | 8 -- src/experiment/arithmeticInfer2.ts | 15 --- src/experiment/arithmeticInfer3.js | 16 --- src/generic/infer.ts | 9 +- src/index.ts | 113 ++++-------------- src/plugins/infer1.ts | 73 ----------- src/plugins/infer2.ts | 74 ------------ src/plugins/infer3.ts | 65 ---------- src/plugins/infer4.ts | 75 ------------ .../{myFirstPlugin.ts => typeInferPlugin.ts} | 25 ++-- tsconfig.json | 2 +- 16 files changed, 68 insertions(+), 518 deletions(-) rename src/experiment/{arithmeticInfer4.ts => arithmeticInfer.ts} (73%) delete mode 100644 src/experiment/arithmeticInfer1.ts delete mode 100644 src/experiment/arithmeticInfer2.ts delete mode 100644 src/experiment/arithmeticInfer3.js delete mode 100644 src/plugins/infer1.ts delete mode 100644 src/plugins/infer2.ts delete mode 100644 src/plugins/infer3.ts delete mode 100644 src/plugins/infer4.ts rename src/plugins/{myFirstPlugin.ts => typeInferPlugin.ts} (79%) diff --git a/README.md b/README.md index 897dd0b..a7bf7e4 100644 --- a/README.md +++ b/README.md @@ -11,46 +11,28 @@ pnpm build-and-run ## experiment -See: the section under `/src/experiment` and `/src/plugins`. +Have a look at the section under `/src/experiment` and `/src/plugins`: + +- `src/plugins/typeInferPlugin.ts` is the actual plugin +- in `tsconfig.json` we configure TypeScript to run the plugin +- `src/experiment/arithmeticInfer.ts` with an example where we define `__infer__` +- `build/experiment/arithmeticInfer.ts` where the `__infer__` string literal is replace with the actual types ### The idea -Create a TypeScript plugin which can replace structures like: +Create a TypeScript plugin which can replace a string literal like `__infer__` in a typed-function definition: - infer(factoryFunction) + typed('square', '__infer__', (dep: { ... } => { ... }) -where `factoryFunction` is a mathjs factory function in TypeScript, with something like: - - infer({ signature: factoryFunction }) - -where `signature` is a string containing the type of the factory function and its dependencies. - -Relevant methods of the TypeScript compiler are: +with the actual types, something like: -```ts -const program = ts.createProgram(fileNames, options) -const typeChecker = program.getTypeChecker() - -// relevant methods: -// -// typeChecker.getSymbolAtLocation -// typeChecker.getTypeOfSymbolAtLocation -// typeChecker.getResolvedSignature -// typeChecker.getSignaturesOfType -``` - -### Status - -None of the experiments (`infer1` and `infer2`) are outputting something useful yet. + typed('square', '{ deps: { multiply: (a: T, b: T) => T; }; return: (a: T) => T }', (dep: { ... } => { ... }) +(We can discuss what syntax we like most, this is just a POC) ### How to run - pnpm experiment:infer1 - pnpm experiment:infer1-direct - pnpm experiment:infer2 - pnpm experiment:infer3 - pnpm experiment:infer4 + pnpm build-and-run ### Read more diff --git a/package.json5 b/package.json5 index 1f3d6e0..02bb592 100644 --- a/package.json5 +++ b/package.json5 @@ -4,14 +4,7 @@ description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept', main: 'index.ts', scripts: { - 'build-and-run': 'ttsc -b && node build', - 'experiment:infer1': 'ttsc -b && node build/plugins/infer1.js ./src/generic/arithmetic.ts', - 'experiment:infer1-direct': 'ttsc -b && node build/plugins/infer1.js ./src/experiment/arithmeticInfer1.ts', - 'experiment:infer2': 'ttsc -b && node build/plugins/infer2.js ./src/experiment/arithmeticInfer2.ts', - 'experiment:infer3': 'ttsc -b && node build/plugins/infer3.js ./src/experiment/arithmeticInfer3.js', - 'experiment:infer4': 'ttsc -b && node build/plugins/infer4.js ./src/experiment/arithmeticInfer4.ts', - 'experiment:infer4:plugin': 'ttsc -b', - test: 'echo "Error: no test specified" && exit 1', + 'build-and-run': 'ttsc -b && node build/index.js', }, keywords: [ 'math', @@ -25,12 +18,9 @@ url: 'https://code.studioinfinity.org/glen/typocomath.git', }, dependencies: { - 'reflect-metadata': '0.1.13', - 'source-map': '^0.7.4', - 'typescript-rtti': '0.8.3', + '@types/node': '20.5.7', }, devDependencies: { - '@types/node': '18.11.18', 'ts-node': '10.9.1', ttypescript: '1.5.15', typescript: '4.7.4', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 47c4737..fdeb100 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,22 +1,16 @@ lockfileVersion: 5.4 specifiers: - '@types/node': 18.11.18 - reflect-metadata: 0.1.13 - source-map: ^0.7.4 + '@types/node': 20.5.7 ts-node: 10.9.1 ttypescript: 1.5.15 typescript: 4.7.4 - typescript-rtti: 0.8.3 dependencies: - reflect-metadata: 0.1.13 - source-map: 0.7.4 - typescript-rtti: 0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu + '@types/node': 20.5.7 devDependencies: - '@types/node': 18.11.18 - ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi + ts-node: 10.9.1_l7whiu4appksmcywzzf5ucsgha ttypescript: 1.5.15_6oasmw356qmm23djlsjgkwvrtm typescript: 4.7.4 @@ -61,9 +55,8 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true - /@types/node/18.11.18: - resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} - dev: true + /@types/node/20.5.7: + resolution: {integrity: sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==} /acorn-walk/8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} @@ -114,10 +107,6 @@ packages: 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.4: resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} hasBin: true @@ -127,17 +116,12 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true - /source-map/0.7.4: - resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} - engines: {node: '>= 8'} - dev: false - /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /ts-node/10.9.1_nv75g3i7xuh23du6z7qul3uiqi: + /ts-node/10.9.1_l7whiu4appksmcywzzf5ucsgha: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -156,7 +140,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 18.11.18 + '@types/node': 20.5.7 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 @@ -176,25 +160,15 @@ packages: typescript: '>=3.2.2' dependencies: resolve: 1.22.4 - ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi + ts-node: 10.9.1_l7whiu4appksmcywzzf5ucsgha typescript: 4.7.4 dev: true - /typescript-rtti/0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu: - resolution: {integrity: sha512-uX1A0JKs1o/ptLJqkubRCGgN7NOCYSTKRXyRIjG80exsLrPDq4jJWMfQxlHMAcv/zjoX0V6iIGU7bwjGWTzpLg==} - engines: {node: '>=10'} - peerDependencies: - reflect-metadata: ^0.1.13 - typescript: ^4.5 || ^4.6 || ^4.7 - dependencies: - reflect-metadata: 0.1.13 - typescript: 4.7.4 - dev: false - /typescript/4.7.4: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true + dev: true /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} diff --git a/src/core/Dispatcher.ts b/src/core/Dispatcher.ts index 1da89ff..4ba58fa 100644 --- a/src/core/Dispatcher.ts +++ b/src/core/Dispatcher.ts @@ -1,6 +1,3 @@ -import "reflect-metadata" -import { reflect, type CallSite } from 'typescript-rtti' - /* A Dispatcher is a collection of operations that do run-time * dispatch on the types of their arguments. Thus, every individual * method is like a typed-function (from the library by that name), @@ -51,15 +48,15 @@ export class Dispatcher { console.log('Pretending to install', name, signature, '=>', returns) // @ts-ignore - console.log(name, 'signature', reflect(signature)) - console.log(name, 'dependencies', reflect(dependencies)) + // console.log(name, 'signature', reflect(signature)) + // console.log(name, 'dependencies', reflect(dependencies)) //TODO: implement me } installType(name: TypeName, typespec: TypeSpecification) { console.log('Pretending to install type', name, typespec) //TODO: implement me } - constructor(collection: SpecificationsGroup) { + constructor(collection: SpecificationsGroup) { for (const key in collection) { console.log('Working on', key) for (const identifier in collection[key]) { diff --git a/src/experiment/arithmeticInfer4.ts b/src/experiment/arithmeticInfer.ts similarity index 73% rename from src/experiment/arithmeticInfer4.ts rename to src/experiment/arithmeticInfer.ts index dc2f45a..e17ac6b 100644 --- a/src/experiment/arithmeticInfer4.ts +++ b/src/experiment/arithmeticInfer.ts @@ -1,6 +1,6 @@ import { typed } from '../generic/infer' -export const square = typed('__infer__', (dep: { +export const square = typed('square', '__infer__', (dep: { multiply: (a: T, b: T) => T, unaryMinus: (x: T) => T, // just for the experiment }): (a: T) => T => diff --git a/src/experiment/arithmeticInfer1.ts b/src/experiment/arithmeticInfer1.ts deleted file mode 100644 index 92aeffd..0000000 --- a/src/experiment/arithmeticInfer1.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {infer} from '../generic/infer' - -export const square = infer((dep: { - multiply: (a: number, b: number) => number, - unaryMinus: (x: number) => number, // just for the experiment -}): (a: number) => number => - z => dep.multiply(z, z) -) diff --git a/src/experiment/arithmeticInfer2.ts b/src/experiment/arithmeticInfer2.ts deleted file mode 100644 index ab4eebf..0000000 --- a/src/experiment/arithmeticInfer2.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { infer } from '../generic/infer' -import { Dependencies, Signature } from '../interfaces/type' - -export type multiplyDep = Dependencies<'multiply', T> - -export const square1 = - (dep: Dependencies<'multiply', T>): Signature<'square', T> => - z => dep.multiply(z, z) - -export const square2 = infer((dep: { - multiply: (a: number, b: number) => number, - unaryMinus: (x: number) => number, // just for the experiment -}): (a: number) => number => - z => dep.multiply(z, z) -) diff --git a/src/experiment/arithmeticInfer3.js b/src/experiment/arithmeticInfer3.js deleted file mode 100644 index 2d0b6da..0000000 --- a/src/experiment/arithmeticInfer3.js +++ /dev/null @@ -1,16 +0,0 @@ -// @ts-check - -/** - * Function square - * - * Description of function square bla bla bla - * - * @param {{ - * multiply: (a: number, b: number) => number, - * unaryMinus: (x: number) => number - * }} dep - * @return {(a: number) => number} - */ -export function square3 (dep) { - return z => dep.multiply(z, z) -} diff --git a/src/generic/infer.ts b/src/generic/infer.ts index 92db72d..4fa9279 100644 --- a/src/generic/infer.ts +++ b/src/generic/infer.ts @@ -3,7 +3,12 @@ export function infer(arg: T) : T { return arg } -export function typed(dep: string, arg: T) : T { - console.error('infer should be replaced with runtime type information by a magic TypeScript plugin') +export function typed(name: string, types: string, arg: T) : T { + // TODO: implement typed-function for real + if (types === '__infer__') { + console.error('__infer__ should be replaced with runtime type information by the TypeScript plugin') + } + + console.log(`Creating typed-function "${name}" with types ${types}`) return arg } diff --git a/src/index.ts b/src/index.ts index ce04c21..9fd1e68 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,99 +1,28 @@ -import 'reflect-metadata' +import { Complex } from './Complex/type.js' +import { absquare as absquare_complex } from './Complex/arithmetic.js' +import { square } from './experiment/arithmeticInfer.js' -import {Dispatcher} from './core/Dispatcher.js' -import * as specifications from './all.js' -import {Complex} from './Complex/type.js' -import {absquare as absquare_complex} from './Complex/arithmetic.js' - -import { CallSite, ReflectedObjectRef, reflect } from 'typescript-rtti' -import { square } from './generic/arithmetic.js' - -// verify that typescript-rtti works (just as experiment) -const add = (a: number, b: number): number => a + b -console.log('reflect add') -console.log('parameterNames', reflect(add).parameterNames) -console.log('parameterTypes', reflect(add).parameterTypes.map(type => type.toString())) -console.log('returnType', reflect(add).returnType.toString()) -console.log() -// output: -// reflect function add -// parameterNames [ 'a', 'b' ] -// parameterTypes [ 'class Number', 'class Number' ] -// returnType class Number - -// try out a very simple case (just as experiment) -function createSquare(deps: { - multiply: (a: number, b: number) => number, - subtract: (a: number, b: number) => number -}) { - return (a: number) => deps.multiply(a, a) -} -console.log('reflect createSquare') -console.log('parameter names', reflect(createSquare).parameters.map(parameter => parameter.name)) -// console.log('parameter[0]', (reflect(createSquare).parameters[0] as ReflectedFunctionParameter)) -// @ts-ignore -console.log('parameterTypes[0]', (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m) -console.log('parameterTypes[0].ref.m[0]', - // @ts-ignore - (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0].n, - // @ts-ignore - (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0] -) -console.log('parameterTypes[0].ref.m[0].t.m', - // @ts-ignore - (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0].t.m -) -// @ts-ignore -// console.log('parameters[0]', reflect(createSquare).parameters[0]) -// FIXME: where to find the information of the types of the dependencies multiply and subtract? - -// Test whether we loose the type information when casting to a generic interface -// Conclusion: we keep the information, that is good. -console.log() -console.log('reflect createFunction') -type MathjsDependencies = Record -type MathjsCreateFunction = (deps: MathjsDependencies) => Function -const createFunction: MathjsCreateFunction = createSquare as MathjsCreateFunction -console.log('parameter names', reflect(createFunction).parameters.map(parameter => parameter.name)) -// @ts-ignore -console.log('parameterTypes[0]', (reflect(createFunction).parameterTypes[0] as ReflectedObjectRef)._ref.m) - -// TODO: more specific definition of Specifications -type Specifications = Record> - -console.log() -console.log('CallSite') -function reflectSpecifications(specifications: Specifications, callSite? : CallSite) { - console.log('specifications', reflect(callSite).parameters[0]) - // @ts-ignore - console.log('specifications', reflect(callSite).parameters[0]._ref) // shows 'numbers', 'Complex, 'complex', 'generic' - // @ts-ignore - console.log('specifications', reflect(callSite).parameters[0]._ref.m - .find(item => item.n === 'generic').t.m) // shows 'square', 'unequal' - // @ts-ignore - console.log('specifications', reflect(callSite).parameters[0]._ref.m - .find(item => item.n === 'generic').t.m - .find(item => item.n === 'square').t.p) // [ { n: 'dep', t: [Function: t], b: undefined, v: null } ] - // @ts-ignore - // FIXME: now, we should be able to get the signature of the multiply dependency of the function square, but how? -} -reflectSpecifications(specifications); - - -// TODO: import all specifications (turned off for debugging purposes) -// export default new Dispatcher(Specifications) - - -const mockRealAdd = (a: number, b: number) => a+b -const mockComplexAbsquare = (z: Complex) => z.re*z.re + z.im*z.im +const add = (a: number, b: number) => a + b +const multiply = (a: number, b: number) => a * b +const unaryMinus = (a: number) => -a +const absquare = (z: Complex) => z.re * z.re + z.im * z.im const quatAbsquare = absquare_complex({ - add: mockRealAdd, - absquare: mockComplexAbsquare + add, + absquare }) -const myabs = quatAbsquare({re: {re: 0, im: 1}, im: {re:2, im: 3}}) -const typeTest: typeof myabs = 7 // check myabs is just a number +const result = quatAbsquare({re: {re: 0, im: 1}, im: {re:2, im: 3}}) +const typeTest: typeof result = 7 // check myabs is just a number console.log() -console.log('Result is', myabs) +console.log('Result is', result) + + +const mySquare = square({ + multiply, + unaryMinus +}) + +console.log() +console.log('mySquare(4)=', mySquare(4)) diff --git a/src/plugins/infer1.ts b/src/plugins/infer1.ts deleted file mode 100644 index 9aea0da..0000000 --- a/src/plugins/infer1.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { readFileSync } from "fs"; -import * as ts from "typescript"; -import { inspect } from 'util' - -export function infer(sourceFile: ts.SourceFile) { - recurse(sourceFile); - - function getType(kind: number) { - switch(kind) { - case ts.SyntaxKind.NumberKeyword: return 'number' - case ts.SyntaxKind.StringKeyword: return 'string' - case ts.SyntaxKind.BooleanKeyword: return 'boolean' - default: return String(ts.SyntaxKind[kind]) // TODO: work out all types - } - } - - function recurse(node: ts.Node) { - if (node.kind === ts.SyntaxKind.Identifier) { - console.log('Identifier', node['escapedText'], ts.SyntaxKind[node.kind]) - } - - // recognize a structure like: - // - // export const square = infer((dep: { - // multiply: (a: number, b: number) => number - // }): (a: number) => number => - // z => dep.multiply(z, z) - // ) - if (node?.['name']?.kind === ts.SyntaxKind.Identifier && node?.['name']['escapedText'] === 'dep') { - // console.log('dep', getType(node['type'].kind), node) - - node['type']?.members?.forEach(member => { - console.log('member', { - name: member.name.escapedText, - parameters: member.type.parameters.map(parameter => { - return parameter.name.escapedText + ': ' + getType(parameter.type.kind) - }), - returns: getType(member.type.type.kind) - }) - }) - } - - // recognize a structure like: - // - // export const square = - // (dep: Dependencies<'multiply' | 'unaryMinus', T>): Signature<'square', T> => - // z => dep.multiply(z, z) - if (node?.['name']?.kind === ts.SyntaxKind.Identifier && node?.['name']['escapedText'] === 'dep') { - // TODO - } - - ts.forEachChild(node, recurse); - } -} - -const fileNames = process.argv.slice(2); -console.log('infer files', fileNames) -fileNames.forEach(fileName => { - // Parse a file - const sourceFile = ts.createSourceFile( - fileName, - readFileSync(fileName).toString(), - ts.ScriptTarget.ES2022, - /*setParentNodes */ true - ); - - console.log('AST', fileName, inspect(sourceFile, { depth: null, colors: true })) - - console.log(sourceFile.text) - console.log() - - infer(sourceFile); -}); diff --git a/src/plugins/infer2.ts b/src/plugins/infer2.ts deleted file mode 100644 index 43fc64b..0000000 --- a/src/plugins/infer2.ts +++ /dev/null @@ -1,74 +0,0 @@ -import * as ts from "typescript" - -// based on: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker - -infer2(process.argv.slice(2), { - target: ts.ScriptTarget.ES5, - module: ts.ModuleKind.CommonJS -}) - -function infer2( - fileNames: string[], - options: ts.CompilerOptions -): void { - const program = ts.createProgram(fileNames, options) - const typeChecker = program.getTypeChecker() - - for (const sourceFile of program.getSourceFiles()) { - if (!sourceFile.isDeclarationFile) { - ts.forEachChild(sourceFile, visit) - } - } - - return - - function visit(node: ts.Node) { - // // Only consider exported nodes - // if (!isNodeExported(node)) { - // return; - // } - - // console.log('Node', node.kind, node?.['name']?.escapedText) - - if (ts.isModuleDeclaration(node)) { - // This is a namespace, visit its children - console.log('check') - ts.forEachChild(node, visit); - } else if (ts.isTypeAliasDeclaration(node)) { - console.log('isTypeAliasDeclaration', node.name.escapedText) - - let symbol = typeChecker.getSymbolAtLocation(node.name); - if (symbol) { - const symbolType = typeChecker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration) - const symbolSignature = typeChecker.getSignaturesOfType(symbolType, ts.SignatureKind.Call) - - // checker.getResolvedSignature(symbol) - console.log('symbol', symbol.getName(), symbolSignature) - - // getTypeOfSymbolAtLocation - // getResolvedSignature - } - } else if (ts.isCallExpression(node)) { - console.log('isCallExpression', node.expression) - } else if (ts.isFunctionDeclaration(node)) { - console.log('isFunctionDeclaration', node.name.escapedText, { typeParameter0: node.typeParameters[0] }) - - if (node.name.escapedText === 'infer') { - const param0 = node.typeParameters[0] - if (ts.isPropertyDeclaration(param0)) { - const symbol = typeChecker.getSymbolAtLocation(param0) - - // TODO: get resolving - - // console.log('getResolvedSignature', typeChecker.getResolvedSignature(node) ) - - // const symbolType = typeChecker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration) - // const symbolSignature = typeChecker.getSignaturesOfType(symbolType, ts.SignatureKind.Call) - // console.log('symbol', symbol.getName(), symbolSignature) - - // console.log('getSignaturesOfType', typeChecker.getSignaturesOfType(param0) - } - } - } - } -} diff --git a/src/plugins/infer3.ts b/src/plugins/infer3.ts deleted file mode 100644 index 5c599db..0000000 --- a/src/plugins/infer3.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { readFileSync } from "fs"; -import * as ts from "typescript"; -import { inspect } from 'util' - -export function infer3(sourceFile: ts.SourceFile) { - recurse(sourceFile); - - function getType(kind: number) { - switch(kind) { - case ts.SyntaxKind.NumberKeyword: return 'number' - case ts.SyntaxKind.StringKeyword: return 'string' - case ts.SyntaxKind.BooleanKeyword: return 'boolean' - case ts.SyntaxKind.JSDoc: return 'jsdoc' - default: return String(ts.SyntaxKind[kind]) // TODO: work out all types - } - } - - function recurse(node: ts.Node) { - if (node.kind === ts.SyntaxKind.Identifier) { - console.log('Identifier', node['escapedText'], ts.SyntaxKind[node.kind]) - } - - if (node['jsDoc']) { - console.log('Found a JSDoc comment:') - // console.log(inspect(node['jsDoc'], { depth: null, colors: true })) - - const fullComment = sourceFile.text.slice(node.pos, node.end) - console.log(fullComment) - // TODO: next steps: - // - either get the types from the TypeScript AST, - // or extract them ourselves with regex or anything from the comment text - // - After that, we have to transform the source file and insert the comments - // as string or object that is runtime accessible in JavaScript - } - - ts.forEachChild(node, recurse); - } -} - -const fileNames = process.argv.slice(2); -console.log('infer files', fileNames) -fileNames.forEach(fileName => { - // Parse a file - const sourceFile = ts.createSourceFile( - fileName, - readFileSync(fileName).toString(), - ts.ScriptTarget.ES2022, - /*setParentNodes */ true - ); - - console.log('FILE') - console.log(fileName) - console.log() - - console.log('SOURCE') - console.log(sourceFile.text) - console.log() - - console.log('AST') - console.log(inspect(sourceFile, { depth: null, colors: true })) - console.log() - - console.log('INFER') - infer3(sourceFile); -}); diff --git a/src/plugins/infer4.ts b/src/plugins/infer4.ts deleted file mode 100644 index c5e7834..0000000 --- a/src/plugins/infer4.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { readFileSync } from "fs"; -import * as ts from "typescript"; -import { inspect } from 'util' - -export function infer(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) { - recurse(sourceFile); - - function recurse(node: ts.Node) { - if (ts.isCallExpression(node)) { - if (ts.isIdentifier(node.expression) && node.expression.escapedText === 'infer') { - const argNode = node.arguments[0] - - if (ts.isArrowFunction(argNode)) { - const text = sourceFile.text.slice(argNode.pos, argNode.end) - console.log('infer', text) - - console.log('AST') - console.log(node) - console.log() - - const returnType = argNode.type.getText(sourceFile) - console.log('returnType', returnType) - // (a: number) => number - - const paramNode = argNode.parameters[0] - - const paramType = paramNode.type.getText(sourceFile) - console.log('paramType', paramType) // (a: number) => number - // { - // multiply: (a: number, b: number) => number, - // unaryMinus: (x: number) => number, // just for the experiment - // } - - const type = typeChecker.getTypeAtLocation(paramNode) - const typeStr = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) - console.log('paramTypeString', typeStr) - // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } - } - - } - } - - ts.forEachChild(node, recurse); - } -} - -const fileNames = process.argv.slice(2); -console.log('infer files', fileNames) - -const options = { - target: ts.ScriptTarget.ES2022, - module: ts.ModuleKind.ES2022 -} -let program = ts.createProgram(fileNames, options); -const checker = program.getTypeChecker() - -for (const sourceFile of program.getSourceFiles()) { - if (!sourceFile.isDeclarationFile) { - console.log('FILE') - console.log(sourceFile.fileName) - console.log() - - console.log('SOURCE') - console.log(sourceFile.text) - console.log() - - // console.log('AST') - // console.log(inspect(sourceFile, { depth: null, colors: true })) - // console.log() - - console.log('INFER') - infer(sourceFile, checker); - - } -} diff --git a/src/plugins/myFirstPlugin.ts b/src/plugins/typeInferPlugin.ts similarity index 79% rename from src/plugins/myFirstPlugin.ts rename to src/plugins/typeInferPlugin.ts index ba2feb6..0cf6d6f 100644 --- a/src/plugins/myFirstPlugin.ts +++ b/src/plugins/typeInferPlugin.ts @@ -1,4 +1,4 @@ -import * as ts from 'typescript'; +import ts from 'typescript' const transformer: ts.TransformerFactory = context => { // TODO: get a reference to the program instance that the plugin is running in instead of creating a new program? @@ -7,24 +7,23 @@ const transformer: ts.TransformerFactory = context => { return sourceFile => { // For the experiment we only want to influence a single file - if (!sourceFile.fileName.endsWith('arithmeticInfer4.ts')) { + if (!sourceFile.fileName.endsWith('arithmeticInfer.ts')) { return sourceFile } const visitor = (node: ts.Node): ts.Node => { // @ts-ignore if (ts.isStringLiteral(node) && node.text === '__infer__') { - console.log('STRING LITERAL', node.text) - console.log(node) + console.log('FOUND AN OCCURRENCE OF __infer__') const parentNode = node.parent - console.log('PARENT') - console.log(parentNode) + // console.log('PARENT') + // console.log(parentNode) // TODO: validate that the parent is indeed a mathjs typed function with deps // @ts-ignore - const argNode = parentNode.arguments[1] + const argNode = parentNode.arguments[2] const returnType = argNode.type.getText(sourceFile) console.log('RETURN TYPE') console.log(returnType) @@ -48,11 +47,11 @@ const transformer: ts.TransformerFactory = context => { return ts.factory.createStringLiteral(depsAndReturnType) } - return ts.visitEachChild(node, visitor, context); - }; + return ts.visitEachChild(node, visitor, context) + } - return ts.visitNode(sourceFile, visitor); - }; -}; + return ts.visitNode(sourceFile, visitor) + } +} -export default transformer; +export default transformer diff --git a/tsconfig.json b/tsconfig.json index 50df804..c6b3a50 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,7 @@ "moduleResolution": "Node", "module": "commonjs", "plugins": [{ - "transform": "./src/plugins/myFirstPlugin.ts", + "transform": "./src/plugins/typeInferPlugin.ts", "type": "raw" }] } From d50c1a9ccf006ed5227bc8c8046b0a924cb8c7cf Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 1 Sep 2023 18:36:50 +0200 Subject: [PATCH 15/22] some refinements in the plugin --- README.md | 2 +- src/{generic/infer.ts => core/typed.ts} | 7 +-- src/experiment/arithmeticInfer.ts | 2 +- src/plugins/typeInferPlugin.ts | 57 ++++++++++++------------- 4 files changed, 31 insertions(+), 37 deletions(-) rename src/{generic/infer.ts => core/typed.ts} (54%) diff --git a/README.md b/README.md index a7bf7e4..968cf51 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Have a look at the section under `/src/experiment` and `/src/plugins`: - `src/plugins/typeInferPlugin.ts` is the actual plugin - in `tsconfig.json` we configure TypeScript to run the plugin - `src/experiment/arithmeticInfer.ts` with an example where we define `__infer__` -- `build/experiment/arithmeticInfer.ts` where the `__infer__` string literal is replace with the actual types +- after running TypeScript: look at `build/experiment/arithmeticInfer.ts` where the `__infer__` string literal is replaced with the actual types ### The idea diff --git a/src/generic/infer.ts b/src/core/typed.ts similarity index 54% rename from src/generic/infer.ts rename to src/core/typed.ts index 4fa9279..f66a4ad 100644 --- a/src/generic/infer.ts +++ b/src/core/typed.ts @@ -1,14 +1,9 @@ -export function infer(arg: T) : T { - console.error('infer should be replaced with runtime type information by a magic TypeScript plugin') - return arg -} - export function typed(name: string, types: string, arg: T) : T { // TODO: implement typed-function for real if (types === '__infer__') { console.error('__infer__ should be replaced with runtime type information by the TypeScript plugin') } - console.log(`Creating typed-function "${name}" with types ${types}`) + console.log(`TYPED-FUNCTION: Creating function "${name}" with types ${types}`) return arg } diff --git a/src/experiment/arithmeticInfer.ts b/src/experiment/arithmeticInfer.ts index e17ac6b..be544a3 100644 --- a/src/experiment/arithmeticInfer.ts +++ b/src/experiment/arithmeticInfer.ts @@ -1,4 +1,4 @@ -import { typed } from '../generic/infer' +import { typed } from '../core/typed.js' export const square = typed('square', '__infer__', (dep: { multiply: (a: T, b: T) => T, diff --git a/src/plugins/typeInferPlugin.ts b/src/plugins/typeInferPlugin.ts index 0cf6d6f..4ca6c21 100644 --- a/src/plugins/typeInferPlugin.ts +++ b/src/plugins/typeInferPlugin.ts @@ -6,45 +6,44 @@ const transformer: ts.TransformerFactory = context => { const checker = program.getTypeChecker() return sourceFile => { - // For the experiment we only want to influence a single file - if (!sourceFile.fileName.endsWith('arithmeticInfer.ts')) { - return sourceFile - } - const visitor = (node: ts.Node): ts.Node => { // @ts-ignore if (ts.isStringLiteral(node) && node.text === '__infer__') { - console.log('FOUND AN OCCURRENCE OF __infer__') + console.log('PLUGIN: FOUND AN OCCURRENCE OF __infer__') + // we're looking for a function call like typed('name', '__infer__', deps => ...) const parentNode = node.parent - // console.log('PARENT') - // console.log(parentNode) + if (ts.isCallExpression(parentNode) && ts.isIdentifier(parentNode.expression) && parentNode.expression.escapedText === 'typed') { + // console.log('PARENT') + // console.log(parentNode) - // TODO: validate that the parent is indeed a mathjs typed function with deps + // TODO: validate that argNode is an ArrowFunction + // @ts-ignore + const argNode = parentNode.arguments[2] + // @ts-ignore + const returnType = argNode.type.getText(sourceFile) + console.log('PLUGIN: RETURN TYPE') + console.log(returnType) + // (a: number) => number - // @ts-ignore - const argNode = parentNode.arguments[2] - const returnType = argNode.type.getText(sourceFile) - console.log('RETURN TYPE') - console.log(returnType) - // (a: number) => number + // @ts-ignore + const paramNode = argNode.parameters[0] + const paramTypeSrc = paramNode.type.getText(sourceFile) + console.log('PLUGIN: PARAM TYPE SRC', paramTypeSrc) + // { + // multiply: (a: number, b: number) => number, + // unaryMinus: (x: number) => number, // just for the experiment + // } - const paramNode = argNode.parameters[0] - const paramTypeSrc = paramNode.type.getText(sourceFile) - console.log('PARAM TYPE SRC', paramTypeSrc) - // { - // multiply: (a: number, b: number) => number, - // unaryMinus: (x: number) => number, // just for the experiment - // } + const type = checker.getTypeAtLocation(paramNode) + const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) + console.log('PLUGIN: PARAM TYPE STRING', paramType) + // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } - const type = checker.getTypeAtLocation(paramNode) - const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) - console.log('PARAM TYPE STRING', paramType) - // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } + const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` - const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` - - return ts.factory.createStringLiteral(depsAndReturnType) + return ts.factory.createStringLiteral(depsAndReturnType) + } } return ts.visitEachChild(node, visitor, context) From 3967f5ce2b12717f42c3b5845d14deab91942ab7 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 1 Sep 2023 18:43:03 +0200 Subject: [PATCH 16/22] let TypeScript output ES modules --- package.json5 | 2 +- tsconfig.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json5 b/package.json5 index 02bb592..4e03b9b 100644 --- a/package.json5 +++ b/package.json5 @@ -4,7 +4,7 @@ description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept', main: 'index.ts', scripts: { - 'build-and-run': 'ttsc -b && node build/index.js', + 'build-and-run': 'ttsc -b && echo {"type":"module"} > build/package.json && node build/index.js', }, keywords: [ 'math', diff --git a/tsconfig.json b/tsconfig.json index c6b3a50..eed8f17 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,6 @@ "allowJs": false, "noImplicitAny": false, "moduleResolution": "Node", - "module": "commonjs", "plugins": [{ "transform": "./src/plugins/typeInferPlugin.ts", "type": "raw" From 26631febf905cca9a257131c0a8f5589143ad641 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 1 Sep 2023 20:54:39 +0200 Subject: [PATCH 17/22] add a TODO explaining the plan --- src/core/typed.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/typed.ts b/src/core/typed.ts index f66a4ad..3306776 100644 --- a/src/core/typed.ts +++ b/src/core/typed.ts @@ -5,5 +5,7 @@ export function typed(name: string, types: string, arg: T) : T { } console.log(`TYPED-FUNCTION: Creating function "${name}" with types ${types}`) + + // TODO: here we can now turn the inputs into a real typed-function with a signature return arg } From 7fc9d2a2f360742c262934ed0443d08a54502d16 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 8 Sep 2023 14:10:03 +0200 Subject: [PATCH 18/22] remove name argument from the reflect function --- src/core/{typed.ts => $reflect.ts} | 4 ++-- src/experiment/arithmeticInfer.ts | 4 ++-- src/plugins/typeInferPlugin.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/core/{typed.ts => $reflect.ts} (65%) diff --git a/src/core/typed.ts b/src/core/$reflect.ts similarity index 65% rename from src/core/typed.ts rename to src/core/$reflect.ts index 3306776..abd069d 100644 --- a/src/core/typed.ts +++ b/src/core/$reflect.ts @@ -1,10 +1,10 @@ -export function typed(name: string, types: string, arg: T) : T { +export function $reflect(types: string, arg: T) : T { // TODO: implement typed-function for real if (types === '__infer__') { console.error('__infer__ should be replaced with runtime type information by the TypeScript plugin') } - console.log(`TYPED-FUNCTION: Creating function "${name}" with types ${types}`) + console.log(`INFER: Creating function with types ${types}`) // TODO: here we can now turn the inputs into a real typed-function with a signature return arg diff --git a/src/experiment/arithmeticInfer.ts b/src/experiment/arithmeticInfer.ts index be544a3..c36ed92 100644 --- a/src/experiment/arithmeticInfer.ts +++ b/src/experiment/arithmeticInfer.ts @@ -1,6 +1,6 @@ -import { typed } from '../core/typed.js' +import { $reflect } from '../core/$reflect.js' -export const square = typed('square', '__infer__', (dep: { +export const square = $reflect('__infer__', (dep: { multiply: (a: T, b: T) => T, unaryMinus: (x: T) => T, // just for the experiment }): (a: T) => T => diff --git a/src/plugins/typeInferPlugin.ts b/src/plugins/typeInferPlugin.ts index 4ca6c21..08a19ae 100644 --- a/src/plugins/typeInferPlugin.ts +++ b/src/plugins/typeInferPlugin.ts @@ -13,13 +13,13 @@ const transformer: ts.TransformerFactory = context => { // we're looking for a function call like typed('name', '__infer__', deps => ...) const parentNode = node.parent - if (ts.isCallExpression(parentNode) && ts.isIdentifier(parentNode.expression) && parentNode.expression.escapedText === 'typed') { + if (ts.isCallExpression(parentNode) && ts.isIdentifier(parentNode.expression) && parentNode.expression.escapedText === '$reflect') { // console.log('PARENT') // console.log(parentNode) // TODO: validate that argNode is an ArrowFunction // @ts-ignore - const argNode = parentNode.arguments[2] + const argNode = parentNode.arguments[1] // @ts-ignore const returnType = argNode.type.getText(sourceFile) console.log('PLUGIN: RETURN TYPE') From 232d5d4a96b3d9face1ba428224d631d28a44330 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 8 Sep 2023 14:25:14 +0200 Subject: [PATCH 19/22] make defining `__infer__` redundant --- src/core/$reflect.ts | 6 ++-- src/experiment/arithmeticInfer.ts | 2 +- src/plugins/typeInferPlugin.ts | 60 +++++++++++++++---------------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/core/$reflect.ts b/src/core/$reflect.ts index abd069d..ca4f920 100644 --- a/src/core/$reflect.ts +++ b/src/core/$reflect.ts @@ -1,7 +1,7 @@ -export function $reflect(types: string, arg: T) : T { +export function $reflect(arg: T, types?: string) : T { // TODO: implement typed-function for real - if (types === '__infer__') { - console.error('__infer__ should be replaced with runtime type information by the TypeScript plugin') + if (!types) { + console.error('types should be resolved with runtime type information by the TypeScript plugin') } console.log(`INFER: Creating function with types ${types}`) diff --git a/src/experiment/arithmeticInfer.ts b/src/experiment/arithmeticInfer.ts index c36ed92..4a4a6e6 100644 --- a/src/experiment/arithmeticInfer.ts +++ b/src/experiment/arithmeticInfer.ts @@ -1,6 +1,6 @@ import { $reflect } from '../core/$reflect.js' -export const square = $reflect('__infer__', (dep: { +export const square = $reflect((dep: { multiply: (a: T, b: T) => T, unaryMinus: (x: T) => T, // just for the experiment }): (a: T) => T => diff --git a/src/plugins/typeInferPlugin.ts b/src/plugins/typeInferPlugin.ts index 08a19ae..84c0dcf 100644 --- a/src/plugins/typeInferPlugin.ts +++ b/src/plugins/typeInferPlugin.ts @@ -7,43 +7,43 @@ const transformer: ts.TransformerFactory = context => { return sourceFile => { const visitor = (node: ts.Node): ts.Node => { + // we're looking for a function call like $reflect(deps => ...) // @ts-ignore - if (ts.isStringLiteral(node) && node.text === '__infer__') { - console.log('PLUGIN: FOUND AN OCCURRENCE OF __infer__') + if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.escapedText === '$reflect') { + console.log('PLUGIN: FOUND AN OCCURRENCE OF $reflect') + // console.log('PARENT') + // console.log(node) - // we're looking for a function call like typed('name', '__infer__', deps => ...) - const parentNode = node.parent - if (ts.isCallExpression(parentNode) && ts.isIdentifier(parentNode.expression) && parentNode.expression.escapedText === '$reflect') { - // console.log('PARENT') - // console.log(parentNode) + // TODO: validate that argNode is an ArrowFunction + // @ts-ignore + const argNode = node.arguments[0] + // @ts-ignore + const returnType = argNode.type.getText(sourceFile) + console.log('PLUGIN: RETURN TYPE') + console.log(returnType) + // (a: number) => number - // TODO: validate that argNode is an ArrowFunction - // @ts-ignore - const argNode = parentNode.arguments[1] - // @ts-ignore - const returnType = argNode.type.getText(sourceFile) - console.log('PLUGIN: RETURN TYPE') - console.log(returnType) - // (a: number) => number + // @ts-ignore + const paramNode = argNode.parameters[0] + const paramTypeSrc = paramNode.type.getText(sourceFile) + console.log('PLUGIN: PARAM TYPE SRC', paramTypeSrc) + // { + // multiply: (a: number, b: number) => number, + // unaryMinus: (x: number) => number, // just for the experiment + // } - // @ts-ignore - const paramNode = argNode.parameters[0] - const paramTypeSrc = paramNode.type.getText(sourceFile) - console.log('PLUGIN: PARAM TYPE SRC', paramTypeSrc) - // { - // multiply: (a: number, b: number) => number, - // unaryMinus: (x: number) => number, // just for the experiment - // } + const type = checker.getTypeAtLocation(paramNode) + const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) + console.log('PLUGIN: PARAM TYPE STRING', paramType) + // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } - const type = checker.getTypeAtLocation(paramNode) - const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) - console.log('PLUGIN: PARAM TYPE STRING', paramType) - // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } + const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` - const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` + // Now we insert a second argument to the $reflect function call: a string with the types + // @ts-ignore + node.arguments.push(ts.factory.createStringLiteral(depsAndReturnType)) - return ts.factory.createStringLiteral(depsAndReturnType) - } + return node } return ts.visitEachChild(node, visitor, context) From 24bcf34b6095e8237666661536158503599fa765 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Mon, 11 Sep 2023 16:07:52 +0200 Subject: [PATCH 20/22] try getting resolved type arguments (WIP) --- pnpm-lock.yaml | 11 +++++- src/experiment/arithmeticInfer.ts | 19 +++++++--- src/plugins/typeInferPlugin.ts | 58 +++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fdeb100..533a215 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2,12 +2,14 @@ lockfileVersion: 5.4 specifiers: '@types/node': 20.5.7 + ts-macros: 2.4.1 ts-node: 10.9.1 ttypescript: 1.5.15 typescript: 4.7.4 dependencies: '@types/node': 20.5.7 + ts-macros: 2.4.1_typescript@4.7.4 devDependencies: ts-node: 10.9.1_l7whiu4appksmcywzzf5ucsgha @@ -121,6 +123,14 @@ packages: engines: {node: '>= 0.4'} dev: true + /ts-macros/2.4.1_typescript@4.7.4: + resolution: {integrity: sha512-DmrftFZ5pgM7dw8ySYcd90wGEfjp7yx9MFw/YyJHNdwKYdUb//lcy/XI/Lvg3LbrGIbjBMEj1rzuc5Kfkzvafg==} + peerDependencies: + typescript: 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x + dependencies: + typescript: 4.7.4 + dev: false + /ts-node/10.9.1_l7whiu4appksmcywzzf5ucsgha: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true @@ -168,7 +178,6 @@ packages: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true - dev: true /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} diff --git a/src/experiment/arithmeticInfer.ts b/src/experiment/arithmeticInfer.ts index 4a4a6e6..771d37c 100644 --- a/src/experiment/arithmeticInfer.ts +++ b/src/experiment/arithmeticInfer.ts @@ -1,8 +1,19 @@ import { $reflect } from '../core/$reflect.js' +import { Dependencies } from '../interfaces/type.js' -export const square = $reflect((dep: { - multiply: (a: T, b: T) => T, - unaryMinus: (x: T) => T, // just for the experiment -}): (a: T) => T => +// unaryMinus dep is just for the experiment +// FIXME: the typescript plugin should resolve Dependencies<'multiply' | 'unaryMinus', T> +export const square = $reflect((dep: Dependencies<'multiply' | 'unaryMinus', T>): (a: T) => T => z => dep.multiply(z, z) ) + +// export const square2 = function $reflect(dep: Dependencies<'multiply' | 'unaryMinus', T>) { +// return (z: T) => dep.multiply(z, z) +// } + +// export const square = $reflect((dep: { +// multiply: (a: T, b: T) => T, +// unaryMinus: (x: T) => T, // just for the experiment +// }): (a: T) => T => +// z => dep.multiply(z, z) +// ) diff --git a/src/plugins/typeInferPlugin.ts b/src/plugins/typeInferPlugin.ts index 84c0dcf..5e91f07 100644 --- a/src/plugins/typeInferPlugin.ts +++ b/src/plugins/typeInferPlugin.ts @@ -32,11 +32,39 @@ const transformer: ts.TransformerFactory = context => { // unaryMinus: (x: number) => number, // just for the experiment // } + // WIP + // @ts-ignore const type = checker.getTypeAtLocation(paramNode) const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) + // const paramType = checker.typeToString(type) + // TDOO: get checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration) console.log('PLUGIN: PARAM TYPE STRING', paramType) // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } + + // WIP + // For a function definition + // const signature = checker.getResolvedSignature(node); + // if (signature != null) { + // // outputs -- (Ctor: Ctor): void + // console.log(signature) + // console.log('TEST 1', checker.signatureToString(signature)); + // // @ts-ignore + // // console.log('TEST 2', checker.getResolvedSignatureForStringLiteralCompletions(node)); + // const params = signature.getParameters(); + // for (const param of params) { + // const type = checker.getTypeOfSymbolAtLocation(param, node); + // // outputs -- Ctor + // console.log('TEST 3', checker.typeToString(type)); + // } + // } + + // WIP + const type0 = resolveTypeArgumentOfCall(checker, node, 0) + // @ts-ignore + const typeStr = checker.typeToString(type0); + console.log('PLUGIN: RESOLVED TYPE ARGUMENT', typeStr) + const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` // Now we insert a second argument to the $reflect function call: a string with the types @@ -53,4 +81,34 @@ const transformer: ts.TransformerFactory = context => { } } +// WIP +function resolveTypeArgumentOfCall(checker: ts.TypeChecker, macroCall: ts.CallExpression, typeIndex: number) : ts.Type | undefined { + + // @ts-ignore + console.log( 'TEST D', macroCall.arguments[0].typeParameters) + + if (!macroCall.typeArguments || !macroCall.typeArguments[typeIndex]) return; + const type = checker.getTypeAtLocation(macroCall.typeArguments[typeIndex]); + + console.log( 'TEST A', type, macroCall.typeArguments ) + + return type + + // const lastMacroCall = this.getLastMacro(); + // if (!lastMacroCall) return type; + // if (type.isTypeParameter()) { + // const resolvedTypeParameterIndex = lastMacroCall.macro.typeParams.findIndex(arg => this.checker.getTypeAtLocation(arg) === type); + // if (resolvedTypeParameterIndex === -1) return; + // if (lastMacroCall.call && ts.isCallExpression(lastMacroCall.call)) { + // const resolvedTypeParam = lastMacroCall.call.typeArguments?.[resolvedTypeParameterIndex]; + // if (!resolvedTypeParam) return resolveTypeArguments(this.checker, lastMacroCall.call)[resolvedTypeParameterIndex]; + // return this.checker.getTypeAtLocation(resolvedTypeParam); + // } else return; + // } else { + // const allParams = lastMacroCall.macro.typeParams.map(p => this.checker.getTypeAtLocation(p)); + // const replacementTypes = resolveTypeArguments(this.checker, lastMacroCall.call as ts.CallExpression); + // return resolveTypeWithTypeParams(type, allParams, replacementTypes); + // } +} + export default transformer From 94a0737555ab9a8cfe3846673fbee78fe87421ec Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Mon, 11 Sep 2023 16:09:50 +0200 Subject: [PATCH 21/22] update pnpm lock file --- pnpm-lock.yaml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 533a215..fdeb100 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2,14 +2,12 @@ lockfileVersion: 5.4 specifiers: '@types/node': 20.5.7 - ts-macros: 2.4.1 ts-node: 10.9.1 ttypescript: 1.5.15 typescript: 4.7.4 dependencies: '@types/node': 20.5.7 - ts-macros: 2.4.1_typescript@4.7.4 devDependencies: ts-node: 10.9.1_l7whiu4appksmcywzzf5ucsgha @@ -123,14 +121,6 @@ packages: engines: {node: '>= 0.4'} dev: true - /ts-macros/2.4.1_typescript@4.7.4: - resolution: {integrity: sha512-DmrftFZ5pgM7dw8ySYcd90wGEfjp7yx9MFw/YyJHNdwKYdUb//lcy/XI/Lvg3LbrGIbjBMEj1rzuc5Kfkzvafg==} - peerDependencies: - typescript: 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x - dependencies: - typescript: 4.7.4 - dev: false - /ts-node/10.9.1_l7whiu4appksmcywzzf5ucsgha: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true @@ -178,6 +168,7 @@ packages: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true + dev: true /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} From ab7fc4450ea0ef28efa51808dfae043f595b083a Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Mon, 11 Sep 2023 17:50:10 +0200 Subject: [PATCH 22/22] add another try --- package.json5 | 2 +- pnpm-lock.yaml | 80 ++++++++++++++++++---------------- src/plugins/typeInferPlugin.ts | 40 +++-------------- 3 files changed, 49 insertions(+), 73 deletions(-) diff --git a/package.json5 b/package.json5 index 4e03b9b..6c2691d 100644 --- a/package.json5 +++ b/package.json5 @@ -4,7 +4,7 @@ description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept', main: 'index.ts', scripts: { - 'build-and-run': 'ttsc -b && echo {"type":"module"} > build/package.json && node build/index.js', + 'go': 'ttsc && echo {"type":"module"} > build/package.json && node build/index.js', }, keywords: [ 'math', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fdeb100..e931f27 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,113 +1,119 @@ -lockfileVersion: 5.4 +lockfileVersion: '6.0' -specifiers: - '@types/node': 20.5.7 - ts-node: 10.9.1 - ttypescript: 1.5.15 - typescript: 4.7.4 +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false dependencies: - '@types/node': 20.5.7 + '@types/node': + specifier: 20.5.7 + version: 20.5.7 devDependencies: - ts-node: 10.9.1_l7whiu4appksmcywzzf5ucsgha - ttypescript: 1.5.15_6oasmw356qmm23djlsjgkwvrtm - typescript: 4.7.4 + ts-node: + specifier: 10.9.1 + version: 10.9.1(@types/node@20.5.7)(typescript@4.7.4) + ttypescript: + specifier: 1.5.15 + version: 1.5.15(ts-node@10.9.1)(typescript@4.7.4) + typescript: + specifier: 4.7.4 + version: 4.7.4 packages: - /@cspotcode/source-map-support/0.8.1: + /@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.1: + /@jridgewell/resolve-uri@3.1.1: resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/sourcemap-codec/1.4.15: + /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} dev: true - /@jridgewell/trace-mapping/0.3.9: + /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /@tsconfig/node10/1.0.9: + /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true - /@tsconfig/node12/1.0.11: + /@tsconfig/node12@1.0.11: resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} dev: true - /@tsconfig/node14/1.0.3: + /@tsconfig/node14@1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} dev: true - /@tsconfig/node16/1.0.4: + /@tsconfig/node16@1.0.4: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true - /@types/node/20.5.7: + /@types/node@20.5.7: resolution: {integrity: sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==} - /acorn-walk/8.2.0: + /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} dev: true - /acorn/8.10.0: + /acorn@8.10.0: resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} engines: {node: '>=0.4.0'} hasBin: true dev: true - /arg/4.1.3: + /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true - /create-require/1.1.1: + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true - /diff/4.0.2: + /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} dev: true - /function-bind/1.1.1: + /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true - /has/1.0.3: + /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.13.0: + /is-core-module@2.13.0: resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} dependencies: has: 1.0.3 dev: true - /make-error/1.3.6: + /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /path-parse/1.0.7: + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true - /resolve/1.22.4: + /resolve@1.22.4: resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} hasBin: true dependencies: @@ -116,12 +122,12 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true - /supports-preserve-symlinks-flag/1.0.0: + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /ts-node/10.9.1_l7whiu4appksmcywzzf5ucsgha: + /ts-node@10.9.1(@types/node@20.5.7)(typescript@4.7.4): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -152,7 +158,7 @@ packages: yn: 3.1.1 dev: true - /ttypescript/1.5.15_6oasmw356qmm23djlsjgkwvrtm: + /ttypescript@1.5.15(ts-node@10.9.1)(typescript@4.7.4): resolution: {integrity: sha512-48ykDNHzFnPMnv4hYX1P8Q84TvCZyL1QlFxeuxsuZ48X2+ameBgPenvmCkHJtoOSxpoWTWi8NcgNrRnVDOmfSg==} hasBin: true peerDependencies: @@ -160,21 +166,21 @@ packages: typescript: '>=3.2.2' dependencies: resolve: 1.22.4 - ts-node: 10.9.1_l7whiu4appksmcywzzf5ucsgha + ts-node: 10.9.1(@types/node@20.5.7)(typescript@4.7.4) typescript: 4.7.4 dev: true - /typescript/4.7.4: + /typescript@4.7.4: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true dev: true - /v8-compile-cache-lib/3.0.1: + /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true - /yn/3.1.1: + /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} dev: true diff --git a/src/plugins/typeInferPlugin.ts b/src/plugins/typeInferPlugin.ts index 5e91f07..d4fbe94 100644 --- a/src/plugins/typeInferPlugin.ts +++ b/src/plugins/typeInferPlugin.ts @@ -35,7 +35,7 @@ const transformer: ts.TransformerFactory = context => { // WIP // @ts-ignore const type = checker.getTypeAtLocation(paramNode) - const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) + const paramType = checker.typeToString(type, undefined, ts.TypeFormatFlags.InTypeAlias) // const paramType = checker.typeToString(type) // TDOO: get checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration) console.log('PLUGIN: PARAM TYPE STRING', paramType) @@ -60,10 +60,10 @@ const transformer: ts.TransformerFactory = context => { // } // WIP - const type0 = resolveTypeArgumentOfCall(checker, node, 0) - // @ts-ignore - const typeStr = checker.typeToString(type0); - console.log('PLUGIN: RESOLVED TYPE ARGUMENT', typeStr) + const type1 = checker.getTypeAtLocation(paramNode) + const type2 = checker.getApparentType(type1) + const typeStr = checker.typeToString(type2, undefined, ts.TypeFormatFlags.InTypeAlias) + console.log('PLUGIN: RESOLVED TYPE ARGUMENT', typeStr) // TODO: not yet working const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` @@ -81,34 +81,4 @@ const transformer: ts.TransformerFactory = context => { } } -// WIP -function resolveTypeArgumentOfCall(checker: ts.TypeChecker, macroCall: ts.CallExpression, typeIndex: number) : ts.Type | undefined { - - // @ts-ignore - console.log( 'TEST D', macroCall.arguments[0].typeParameters) - - if (!macroCall.typeArguments || !macroCall.typeArguments[typeIndex]) return; - const type = checker.getTypeAtLocation(macroCall.typeArguments[typeIndex]); - - console.log( 'TEST A', type, macroCall.typeArguments ) - - return type - - // const lastMacroCall = this.getLastMacro(); - // if (!lastMacroCall) return type; - // if (type.isTypeParameter()) { - // const resolvedTypeParameterIndex = lastMacroCall.macro.typeParams.findIndex(arg => this.checker.getTypeAtLocation(arg) === type); - // if (resolvedTypeParameterIndex === -1) return; - // if (lastMacroCall.call && ts.isCallExpression(lastMacroCall.call)) { - // const resolvedTypeParam = lastMacroCall.call.typeArguments?.[resolvedTypeParameterIndex]; - // if (!resolvedTypeParam) return resolveTypeArguments(this.checker, lastMacroCall.call)[resolvedTypeParameterIndex]; - // return this.checker.getTypeAtLocation(resolvedTypeParam); - // } else return; - // } else { - // const allParams = lastMacroCall.macro.typeParams.map(p => this.checker.getTypeAtLocation(p)); - // const replacementTypes = resolveTypeArguments(this.checker, lastMacroCall.call as ts.CallExpression); - // return resolveTypeWithTypeParams(type, allParams, replacementTypes); - // } -} - export default transformer