└── README.md
/README.md:
--------------------------------------------------------------------------------
1 | # Babel syntax proposal: Double colon type declarations
2 |
3 | JavaScript / Flow syntax proposal. Separate declaration and implementation of functions, variables, ... in order to improve developer experience when writing typed JS code.
4 |
5 | The idea for this syntax has been inspired by the syntax of [elm](http://elm-lang.org/), [haskell](http://www.haskell.org/) and the comments in the snippets shown in [JavaScript without loops](http://jrsinclair.com/articles/2017/javascript-without-loops/).
6 |
7 | Check out [Professor Frisby's Mostly Adequate Guide to Functional Programming, Chapter 7](https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch7.html) if you seek further reading. The guide is about JavaScript, using the same syntax, but just in comments.
8 |
9 | *Disclaimer: Like many language features this one is also subject to personal preference and you might or might not like it.*
10 |
11 |
12 | ## What it is
13 |
14 | Static typing in JavaScript is a nice and powerful opt-in feature. Both TypeScript and Flow use the same syntax of inlining the type declarations as part of the actual implementation code. While that works well in general, it also tends to make the code slightly harder to read, as it happens in C, C++, Java, ..., too.
15 |
16 | There is a different way favored by popular functional programming languages which chose to go a different way: Separating the declaration and the implementation.
17 |
18 | This proposal covers adding this syntax to JavaScript using the `::` operator:
19 |
20 | ```js
21 | minimum :: (...number) => number
22 |
23 | function minimum (...args) {
24 | return args.reduce(
25 | (min, arg) => arg < min ? arg : min,
26 | 0
27 | )
28 | }
29 | ```
30 |
31 | ## Benefits
32 |
33 | 1. It is easier to add to existing non-typed code, since you do not even need to touch the actual code. You just need to add the declaration.
34 |
35 | 2. It becomes trivial to track / recognize API changes. As soon as you split declaration and implementation you can track breaking interface changes by just watching the declarations and use this knowledge for semantic versioning. Elm, for example, does automatic semantic versioning based on this.
36 |
37 | 3. For functions having many parameters with complex types it can increase readability, since a medium-length declaration line and a medium-length implementation line are easier to grasp than a single long line including both.
38 |
39 | 4. When passing an object as a function parameter you can specify the object properties' types more elegantly. Consider a functional React component, for instance:
40 |
41 | ```js
42 | Greeting :: ({ name: string }) => React.Element
43 |
44 | function Greeting ({ name }) {
45 | return
Hello, {name}!
46 | }
47 |
48 | // You could not write `function Greeting ({ name: string }): React.Element {`,
49 | // since `{ name: string }` would be interpreted as a `const string = props.name`.
50 | ```
51 |
52 |
53 | ## What it is not
54 |
55 | This syntax is intended to be consumed by flow/babel and other code analyzers/transpilers. It is not intended to become an ES core language feature.
56 |
57 |
58 | ## Try it yourself
59 |
60 | Check out [gear](https://github.com/andywer/gear). It is a small tool that makes Babel and Flow work with those types. Futhermore its own code uses this very syntax extension, so check out its code to see a working example of code typed this way.
61 |
62 |
63 | ## Manual setup
64 |
65 | Babel as well as its parser Babylon had to be patched/extended in order to parse the double-colon type syntax. You can find the Babylon fork [here](https://github.com/andywer/babylon/tree/feature/dblcolon-types). Clone the feature branch and run `yarn && npm run build && npm link`.
66 |
67 | The Babel fork can be found [here](https://github.com/andywer/babel/tree/feature/dctypes). Clone the feature branch and run `yarn && lerna bootstrap && npm run build` in the project root directory, then `cd packages/babel-core && rm -rf node_modules/babylon && npm link babylon`. Finally `cd ../babel-types && npm link`.
68 |
69 | To set up the samples in `dctypes-test/` in the babel root directory:
70 |
71 | ```sh
72 | cd dctypes-test/
73 | npm install
74 | rm -rf node_modules/babylon node_modules/babel-types
75 | npm link babylon
76 | npm link babel-types
77 | ```
78 |
79 | `npm run` will show you the samples to run.
80 |
81 |
82 | ## Available transformations
83 |
84 | ### babel-plugin-transform-to-flow
85 |
86 | #### Source
87 |
88 | ```js
89 | // test-code.js
90 |
91 | name :: string
92 |
93 | let name = 'Harry'
94 |
95 | add :: (number, number) => number
96 |
97 | function add (x, y) {
98 | return x + y
99 | }
100 | ```
101 |
102 | #### Command
103 |
104 | ```sh
105 | babel --plugins=transform-dctypes-to-flow --no-babelrc test-code.js
106 | ```
107 |
108 | #### Output
109 |
110 | ```js
111 | /* @flow */
112 |
113 | let name: string = 'Harry';
114 |
115 | function add(x: number, y: number): number {
116 | return x + y;
117 | }
118 | ```
119 |
120 | ### babel-plugin-transform-dctypes-comments
121 |
122 | #### Source
123 |
124 | ```js
125 | // test-code.js
126 |
127 | add :: (number, number) => number
128 |
129 | function add (x, y) {
130 | return x + y
131 | }
132 | ```
133 |
134 | #### Command
135 |
136 | ```sh
137 | babel --plugins=transform-dctypes-comments --no-babelrc test-code.js
138 | ```
139 |
140 | #### Output
141 |
142 | ```js
143 |
144 | // add :: (number, number) => number
145 | function add(x, y) {
146 | return x + y;
147 | }
148 | ```
149 |
150 | ### babel-plugin-transform-dctypes-to-flow & babel-plugin-flow-runtime
151 |
152 | #### Source
153 |
154 | ```js
155 | // test-code.js
156 |
157 | add :: (number, number) => number
158 |
159 | function add (x, y) {
160 | return x + y
161 | }
162 | ```
163 |
164 | #### Command
165 |
166 | ```sh
167 | NODE_ENV=development babel --plugins=transform-dctypes-to-flow,flow-runtime --no-babelrc test-code.js
168 | ```
169 |
170 | #### Output
171 |
172 | ```js
173 | /* @flow */import t from "flow-runtime";
174 |
175 |
176 | function add(x: number, y: number): number {
177 | let _xType = t.number();
178 |
179 | let _yType = t.number();
180 |
181 | const _returnType = t.return(t.number());
182 |
183 | t.param("x", _xType).assert(x);
184 | t.param("y", _yType).assert(y);
185 |
186 | return _returnType.assert(x + y);
187 | }
188 | t.annotate(add, t.function(t.param("x", t.number()), t.param("y", t.number()), t.return(t.number())));
189 | ```
190 |
191 |
192 | ## Limitations
193 |
194 | - Does not yet work with `flow-bin`. The static flow type checker is written in OCAML and it would need to be patched as well.
195 | - Just a proof-of-concept right now. Don't be too mad if it does not work in edge-cases yet.
196 |
197 |
198 | ## Feedback
199 |
200 | Having feedback? Feel free to open an issue or pull request 😉
201 |
--------------------------------------------------------------------------------