Approach6: infer TS from JS template literal #7

Closed
opened 2022-12-24 19:33:55 +00:00 by josdejong · 7 comments
Collaborator

⚠️ Warning: this experiment can potentially drag us in another TypeScript rabbit-hole ⚠️

@glen in the branch approach6_infer_js_to_ts, I've created a file infer.ts with an experiment with a totally different approach:

https://code.studioinfinity.org/glen/typocomath/src/branch/approach6_infer_js_to_ts/src/infer.ts

It's very experimental. My idea is: take the pocomath JavaScript template string and generate TypeScript types from that (inspired by typed-function #123).

afbeelding

If this works out:

  • The pocomath (JS) signature API would be the source-of-truth
  • All "just" works in both JS and TS, without need for typescript-rtti

But:

  • I haven't got multiple dependencies working
  • I'm not sure if this can work with generics like multiply: '(T, T) => T'
  • Passing config isn't yet possible

I think this is worth a bit more fiddling to see if the issues are solvable or not, but we should be careful spending too much time if this is not feasible.

What do you think?

⚠️ Warning: this experiment can potentially drag us in another TypeScript rabbit-hole ⚠️ @glen in the branch `approach6_infer_js_to_ts`, I've created a file `infer.ts` with an experiment with a totally different approach: https://code.studioinfinity.org/glen/typocomath/src/branch/approach6_infer_js_to_ts/src/infer.ts It's very experimental. My idea is: take the pocomath JavaScript template string and generate TypeScript types from that (inspired by [typed-function #123](https://github.com/josdejong/typed-function/issues/123#issuecomment-1207205021)). ![afbeelding](/attachments/4b9ff019-3a0a-4f55-b5a7-8f89d47a1593) If this works out: - The pocomath (JS) signature API would be the source-of-truth - All "just" works in both JS and TS, without need for `typescript-rtti` But: - I haven't got multiple dependencies working - I'm not sure if this can work with generics like `multiply: '(T, T) => T'` - Passing config isn't yet possible I think this is worth a bit more fiddling to see if the issues are solvable or not, but we should be careful spending too much time if this is not feasible. What do you think?
Owner

Isn't this basically what m93a tried initially in his typed-function proof-of-concept? (Sorry I'm feeling too lazy at the moment to look up the link to that for you) I was against that at the time, immediately because there was a redundancy between the typed-function-style annotation and the type that TypeScript should infer from the implementation code itself. Now that I've despaired of actually getting those types to work for us (specifically because of generics), and so we are declaring interfaces and then just having TypeScript verify compliance with them, the repetition here is no worse. You might look back at the discussion with m93a and see if it surfaced any other concerns. Certainly handling generics is one. The other is that it seems a little weird to be devising and implementing a little language for specifying types of things, when TypeScript, that we're planning to use for this next revision of the code, already has such a language built in to it. So why not use that language for the specification and then just extract the information from it that we need? And doesn't the TypeScript mechanism for going from the strings to types use the kind of complicated type-manipulation stuff you've been hoping to avoid? Finally, it looks like the above notation will reiterate the return types on every usage, which I'd certainly prefer to avoid.

So I definitely don't mean to dissuade you from trying to make this work more nicely if you think it's promising, just saying it isn't immediately drawing me in to work on this direction. As far as I am concerned, approach 4.5 is currently the "prototype to beat," so to speak.

Yes, go do Christmas! ;-) See you in a while.

Isn't this basically what m93a tried initially in his typed-function proof-of-concept? (Sorry I'm feeling too lazy at the moment to look up the link to that for you) I was against that at the time, immediately because there was a redundancy between the typed-function-style annotation and the type that TypeScript should infer from the implementation code itself. Now that I've despaired of actually getting those types to work for us (specifically because of generics), and so we are declaring interfaces and then just having TypeScript verify compliance with them, the repetition here is no worse. You might look back at the discussion with m93a and see if it surfaced any other concerns. Certainly handling generics is one. The other is that it seems a little weird to be devising and implementing a little language for specifying types of things, when TypeScript, that we're planning to use for this next revision of the code, already has such a language built in to it. So why not use that language for the specification and then just extract the information from it that we need? And doesn't the TypeScript mechanism for going from the strings to types use the kind of complicated type-manipulation stuff you've been hoping to avoid? Finally, it looks like the above notation will reiterate the return types on every usage, which I'd certainly prefer to avoid. So I definitely don't mean to dissuade you from trying to make this work more nicely if you think it's promising, just saying it isn't immediately drawing me in to work on this direction. As far as I am concerned, approach 4.5 is currently the "prototype to beat," so to speak. Yes, go do Christmas! ;-) See you in a while.
Author
Collaborator

This is a bit of the "opposite" of what we tried in #123: there, we where trying to make typed(...) return a full-fledged TS interface. In this experiment #7, I try to generate a TS interface from an injected dependency, which is "just" inferring a TS type from a string like (number, number) => number for each key in a given object. It would not introduce a new language, but "just" use the little language that pocomath (JS) uses to describe a signature.

At this point I do not think we should pursue this approach (TS rabbithole danger), but still, it would be awesome.

This is a bit of the "opposite" of what we tried in [#123](https://github.com/josdejong/typed-function/issues/123): there, we where trying to make `typed(...)` return a full-fledged TS interface. In this experiment #7, I try to generate a TS interface from an injected dependency, which is "just" inferring a TS type from a string like `(number, number) => number` for each key in a given object. It would not introduce a new language, but "just" use the little language that `pocomath` (JS) uses to describe a signature. At this point I do not think we should pursue this approach (TS rabbithole danger), but still, it would be awesome.
Owner

OK, given your sentiments above and the results of our conversation today, I am closing this issue.

OK, given your sentiments above and the results of our conversation today, I am closing this issue.
glen closed this issue 2022-12-29 15:51:33 +00:00
Author
Collaborator

I gave the template literal type another go. I found a stackoverflow answer that helped a lot, and now the solution works with multiple dependencies and defining the sigature of the function that is being created. The following example is fully typed. See source code of src/infer.js.

afbeelding

@glen this still looks interesting. I'm still not sure if it is feasible in the end though. I want to see if I can get generics to work too, and get the API in a shape more similar to the one in main.

I gave the template literal type another go. I found [a stackoverflow answer](https://stackoverflow.com/questions/68391632/infer-type-from-array-literal) that helped a lot, and now the solution works with multiple dependencies and defining the sigature of the function that is being created. The following example is fully typed. See source code of [src/infer.js](https://code.studioinfinity.org/glen/typocomath/src/branch/approach6_infer_js_to_ts/src/infer.ts). ![afbeelding](/attachments/086fc887-4e3f-42fc-a6b8-095782f7c6bb) @glen this still looks interesting. I'm still not sure if it is feasible in the end though. I want to see if I can get generics to work too, and get the API in a shape more similar to the one in `main`.
Owner

Congrats on getting this to compile and type correctly. Reopening this issue.

I think you know I still have the concerns that this approach will (a) require us to re-implement a portion of the TypeScript type specification language (or worse a subtle variation thereof), which opens up a syncing issue if there are any changes to the underlying language, and just seems like re-doing work that has already been done, and (b) be more redundant than the compiler-plugin based solutions, e.g. above you've said twice that multiply takes two numbers, once in const Multiply = 'multiply(number, number) => number' and again in const multiply (a: number, b: number) => a*b. Plus there are three occurrences of [Mm]ultiply in the code, which seem redundant. Maybe some of these redundancies could be eliminated with some refactoring, but I remain worried.

All that said, if this is the direction you want to go for strictly TypeScript-typing "mathjsNext" code, I'm looking forward to moving the project ahead via any coherent typing strategy. So I remain on standby to put in effort once the typing strategy is determined. Thanks!

Congrats on getting this to compile and type correctly. Reopening this issue. I think you know I still have the concerns that this approach will (a) require us to re-implement a portion of the TypeScript type specification language (or worse a subtle variation thereof), which opens up a syncing issue if there are any changes to the underlying language, and just seems like re-doing work that has already been done, and (b) be more redundant than the compiler-plugin based solutions, e.g. above you've said twice that multiply takes two numbers, once in `const Multiply = 'multiply(number, number) => number'` and again in `const multiply (a: number, b: number) => a*b`. Plus there are three occurrences of [Mm]ultiply in the code, which seem redundant. Maybe some of these redundancies could be eliminated with some refactoring, but I remain worried. All that said, if this is the direction you want to go for strictly TypeScript-typing "mathjsNext" code, I'm looking forward to moving the project ahead via any coherent typing strategy. So I remain on standby to put in effort once the typing strategy is determined. Thanks!
glen reopened this issue 2023-09-14 22:12:56 +00:00
Author
Collaborator

Well, I still have large concerns here too. It requires a lot of effort to get something working, and we still have a lot to figure out.

Next steps are defining interfaces as strings like { multiply: '(T,T)=>T', square: '(T)=>T' }, and then use these interfaces like Dependency<'multiply', number>. Then it would be quite close to the syntax we settled on in the main branch.

I would love this to work out, but right now I think this approach is to fragile and inflexible (if we get all of it to work in the first place).

Well, I still have large concerns here too. It requires a lot of effort to get something working, and we still have a lot to figure out. Next steps are defining interfaces as strings like `{ multiply: '(T,T)=>T', square: '(T)=>T' }`, and then use these interfaces like `Dependency<'multiply', number>`. Then it would be quite close to the syntax we settled on in the `main` branch. I would love this to work out, but right now I think this approach is to fragile and inflexible (if we get all of it to work in the first place).
Author
Collaborator

I'll close this issue again, we're not actively working on this approach and not planning for that either.

I'll close this issue again, we're not actively working on this approach and not planning for that either.
Sign in to join this conversation.
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: glen/typocomath#7
No description provided.