15 comments

[ 3.0 ms ] story [ 23.4 ms ] thread
Cool! I've been wanting something like this for a long time for IDE-suggested type annotations. "From test execution it looks like parameters x and y are always numbers. Add type annotations automatically?"

Or a linter could give optimization recommendations: "Warning: Function deoptimized at runtime because in 2% of calls to this function parameter x is an object."

(comment deleted)
The JavaScript intellisense engine of previous versions of Visual Studio used to instrument a sandboxed JS engine in order to extract the shape of objects and methods used in the code, so it also worked when unusual idioms for structuring code were used. Recent versions use instead the TypeSript compiler, with support for plain JavaScript and flow analysis.
Recent versions of Typescript also pay attention to JSDoc type hints and will even check them for you and start reporting "lint errors" if you just add a `// @ts-check` magic comment to the file. It can be a particularly quick way to bootstrap a project with a lot of JS a file at a time towards static typing. (Personally, I still prefer the "rip off the bandaid" approach of just renaming every JS file to TS and fix compile errors until it builds, but it's great to have options.)
That sounds useful! Maybe helpful for you is that YouCompleteMe for Vim is able to offer some type inference based on usage.
I've recently worked on something similar, which uses dynamic analysis to be able to collect type informations and many other data which is only available at runtime.

Runtime type checking: https://maierfelix.github.io/Iroh/examples/type-checking/

JavaScript -> Flow conversion: https://maierfelix.github.io/Iroh/examples/auto-flow-types/

It's also possible to turn the runtime control-flow of running JavaScript into a playable game: https://maierfelix.github.io/src2game/

Wow, That's some pretty interesting stuff you have there.
Really interesting stuff! I tried out some higher-order examples:

INPUT >>> function add(f, b) { return f(b); };

let a = add(function(x) {return x + 1}, 3);

OUTPUT >>> function add(f: function, b: number): number { return f(b); };

let a: number = add(function(x: number): number {return x + 1}, 3);

and I noticed that the flow type annotations are added to the call site, rather than on the argument f in the definition of add. Are all the types that you generate first order?

The code has to be executed for getting the type information (hence the runtime).

It is less powerful than inferring types, and won't help I think from an IDE perspective, except in the edge case of refining types that are too broad; but it will be based on one execution path.

You could easily use unit tests to get better coverage.
I've done some work on dynamic type checking for JavaScript and the tricky part is always when things start going higher order; sadly this is often omitted from examples.

The motivation for dynamic checking was often because many JavaScript programs couldn't be given any useful static types. With the great work on Flow and TypeScript I'm starting to become convinced that we'll just be able to statically check most JavaScript programs in the future and get decent types out.

> The motivation for dynamic checking was often because many JavaScript programs couldn't be given any useful static types.

I like this quote: “Dynamic typing: The belief that you can’t explain to a computer why your code works, but you can keep track of it all in your head.”

I don't get it personally, when you've got decent type inference the number of type annotations should be minimal and compile-time errors mean things that would cause a runtime crash anyway.