You can indeed generate TypeScript types from a GraphQL schema, using apollo-codegen: https://github.com/apollographql/apollo-codegen
Generative testing from types in TypeScript isn't something I've seen. With a static type system, if the code compiles then you know it's correct with respect to the types - no amount of automated testing would prove otherwise. In a dynamic language like Clojure where there are no types then you need generated values and tests to try and get some (still incomplete) safety. So you want a rich type system which can capture as much information about your data as possible - TypeScript does a nice job of this, on the whole .
Having said that, if you wanted to provide testing data or constraints along side your types, @decorators provide a means of adding custom annotations - so you, or someone could certainly generate test for data beyond the richness of the type system, e.g. a valid email.
Though now I'm curious how the two systems differ, if at all, in their handling of third party libraries that _lack_ type definitions? From slackingoff2017's response below, it sounds like Flow might handle this scenario a bit more gracefully by still providing some level of static checks, whereas TypeScript would give up on checking any usages of the library altogether, correct?
> Generative testing from types in TypeScript isn't something I've seen. With a static type system, if the code compiles then you know it's correct with respect to the types - no amount of automated testing would prove otherwise. In a dynamic language like Clojure where there are no types then you need generated values and tests to try and get some (still incomplete) safety. So you want a rich type system which can capture as much information about your data as possible - TypeScript does a nice job of this, on the whole .
It's certainly true that in a static type system, generative testing can't derive any value from the types alone, and both Flow and TypeScript are static type systems. That was probably the wrong way to frame my question.
I guess what I'm trying to ask is if there's a runtime property specifications system for JavaScript, similar to Clojure Spec, that can specify more than just properties of a system that are statically provable, and can test those specifications using a generative approach.
Typescript is more of a traditional typing system and discourages you from using variable types in the first place.
The important distinction is that if you tell typescript a function can take in any value it makes no attempt to type check. Flow will look at each usage of the function and try to determine if the value being run through is valid.
My personal preference is to typescript so I'm probably biased. Typescript community, tooling, and general ease of use is better. MS has long been known for good dev tools and it shines here.
If you end goal is to statically type your whole application then flow has little or no advantages. In this case it's better to go with Typescript for ease of use, community support, friendlier error warnings, and better tooling. If your goal is to keep using JavaScript and use type checking as a fancy linter then flow is clearly superior.
Something else I noticed that may be very important down the road: typescript is easier to get rid of. The JavaScript it outputs is very readable and mostly looks hand-written. IMO the best tools are the ones easy to get rid of, especially since JS is such a moving target. If Typescript dies one day you can compile down to JS one more time and throw the TS code away and still have a decent codebase. Flow and Babel produce some really crazy stuff, and codegen of clean and readable JavaScript seems like more of an afterthought.
I'm not sure if I'm misinterpreting what you're saying here, but on the face of it, this is not correct. Flow is just annotations over JS, it is not a compile to JS language like TS.
Flow doesn't produce anything. It's absolutely trivial to remove by just stripping the annotations. There is a babel plugin for doing that, but that's all it does. If there's any output after that that doesn't seem to map well to the original JS code, that's all babel on the remaining JS, nothing to do with Flow. It would be the same with or without Flow. Or did you mean something else?
Typescript combines typing and transpilation together, essentially doing what flow+Babel does. Typescript allows you use newer JS features like Babel but produces much cleaner JavaScript.
If the choice is Typescript or flow without Babel Typescript is the winner by a mile. Without Babel hooked up to flow you can't use any new JS features.
A couple of things which mitigate that downside with babel though, at least for my setup & way of working:
(1)
Sourcemaps. For me, the only practical purpose of touching the transpiled code is debugging. For this purpose I find the sourcemaps output by babel gives me 1-1 mapping to my source anyway, pretty flawlessly with chrome dev tools. So, I have no issues with the messiness or non-messiness of the transpiled code.
(2)
With babel, your source is just JS. Eventually, at some glorious point in the future, we will have enough of those great features available natively in enough browsers that it's possible to turn babel transpilation off.
At that point, Flow has a massive ace up its sleeve in terms of being just an additional layer on top of JS - the build step will be incredibly fast and tiny. Typescript on the other hand will never be just JS. There will never be a total 1-1 mapping (TS is, by design philosophy, a superset of JS), and even if it gets really fast there will always be more of a transpilation step, always source mapping, etc. It will never go away.
So, TS feels like a nice packaged solution now, but will it look so attractive in 5 years? Or maybe a touch over engineered for a problem that doesn't really exist any more? In this sense, to me, Flow feels a bit more future-proof.
It's important to keep in mind that the simplicity of TS's ES5 codegen comes with the tradeoff that it isn't spec-compliant.
For example, given a program `for (const el of arr) { }`, TS's codegen is a loop like `for(var i = 0; i < arr.length; i++) { var el = arr[i]; }` whereas Babel's codegen uses `Symbol.iterator`. The former is technically wrong in an environment where `arr` happens to have a custom `[Symbol.iterator]`.
Another example is ES2015 classes, for which Babel's codegen asserts the constructor is being called with `new` whereas TS's doesn't.
> The important distinction is that if you tell typescript a function can take in any value it makes no attempt to type check. Flow will look at each usage of the function and try to determine if the value being run through is valid.
This reads to me to mean that while annotations are optional in both systems, if you don't provide them for a particular subsystem in TypeScript, the static analysis will simply perform no checks for usages of that particular subsystem, while in Flow it will still attempt to validate your subsystem with properties it can infer on a best-effort basis from things like the way its functions are called.
Lately, I've been evaluating Relay Modern for a new project, and one of its new features is the ability for the Relay Compiler to generate Flow type definitions based on your GraphQL schema, which makes for a very compelling case to choose Flow if I were to go with Relay Modern for the project. Are there similar tools for generating TypeScript or Flow type definitions from GraphQL schemas for use in the Redux + Apollo ecosystem?
Also, do either type system have a story for leveraging type definitions to perform generative testing like you could in Clojure with Spec?