foam-framework / foam2

FOAM: Feature-Oriented Active Modeller, Version 2
Apache License 2.0
73 stars 63 forks source link

Typescript Support #1921

Open saulshanabrook opened 5 years ago

saulshanabrook commented 5 years ago

Do you support generating Typescript type annotations based on the models?

kgrgreer commented 5 years ago

No we don't. FOAM doesn't actually generate JS code. Instead it dynamically builds JS prototype and class objects using object and function composition. I realize this is a tricky thing for people to grasp if they aren't familiar with the concept. For Java and Swift we statically generate code which is easy to understand, but for JS we just build up the class and prototype directly by setting slots and using defineProperty. This is much faster as it doesn't involve having to generate and then parse code. You could add a typescript outputter, but then this would decrease the size advantages you get from FOAM (which you might be okay with, given you're not using FOAM now). As way of example, the FOAM Gmail client is almost exactly 100 times smaller than the non-FOAM version. This is because JS is powerful enough to let us expand the models on the client side rather than on the server or as part of the build. However, FOAM does have run-time type-checking where it will check types of arguments and properties if you annotate this information in your model. If you're interested in trying this type of type support instead or, or in addition to, Typescript, then let me know and I'll provide you with some documentation on how to use it.

bshepherdson commented 5 years ago

It's worth pointing out that for compatibility with other, handwritten TS code, you could generate Typescript code from FOAM models that is "weightless". Interfaces and some other type declarations in TS are only used in the TS compiler, and don't survive to the JS output.

saulshanabrook commented 5 years ago

@kgrgreer Thank you for the explanation, this is very interesting. I haven't seen this pattern before in JS so I will have to look into it more to understand it. It actually does make some sense, I am used to this pattern in Python, i.e. using metaprogramming to build useful classes at runtime without having to pre-process.

In Python-land to integrate these kinds of features with static typing requires adding an extension to MyPy (example adding Dataclasses support). Do you think something similar would be required to have this framework integrate nicely with TypeScript?

EDIT: I guess there are two options here for Typescript support without having to pre-generate some typings:

  1. Somehow add a "plugin" to TypeScript that has special support for these concepts (not sure if you can do this).
  2. Make TypeScript expressive enough to be able to express the type creation based on these concepts. I don't know enough about either system to understand how (in)feasible this is. (issue on adding support for class decorators to TS https://github.com/Microsoft/TypeScript/issues/26347). "Increasing expressivity" and "Enabling popular JS patterns in a type-safe way" are both on TypeScript's six month roadmap.
kgrgreer commented 5 years ago

It's been many years since I looked at TypeScript, but doesn't it convert the type checks to run-time asserts using a small library? FOAM does the same thing if you provide types. You can see this done in debug.js. You could do the same thing but call out to the typescript assertion library instead. Although, that wouldn't help your typescript code calling into FOAM. For that you could follow @shepheb's idea above. Or just use FOAM's type checking instead of TypeScript (but you may already have a large investment in TypeScript).

bshepherdson commented 5 years ago

I was thinking about this more, recently.

TypeScript has excellent support for adding compile-time-only types to an existing JavaScript library, since that's a common use case.

Since FOAM already has a type system, and ~all of (user-visible) FOAM is modeled, we could add .d.ts TypeScript declaration files as a codegen target.

Then you could work with FOAM from TypeScript by using the same JS code that builds things dynamically at runtime, but your TS code is statically checked against the generated type declarations. That yields the best of both worlds, with strong type-checking and no added runtime overhead.