File: atdts.rst

package info (click to toggle)
ocaml-atd 2.16.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 7,768 kB
  • sloc: ml: 45,944; python: 827; sh: 339; makefile: 306; cpp: 195; java: 76
file content (371 lines) | stat: -rw-r--r-- 10,599 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
**************************
TypeScript Support - atdts
**************************

This documentation is incomplete. Your help would be appreciated! In
particular, some how-to guides would be great.

..
  The atdts documentation is divided in four parts as advocated by
  Daniele Procida: https://documentation.divio.com/
  I recommend watching the 30-min presentation.

Tutorials
=========

..
  Tutorials are learning-oriented. The reader is taken through a
  series of actions that will directly show them what the tool is
  capable of. Explanations should not be necessary.
  documentation category: practical/exploring

Hello World
-----------

Install ``atdts`` with ``opam``::

  opam install atdts

Create a file ``hello.atd`` containing this:

.. code-block:: ocaml

  type message = {
    subject: string;
    body: string;
  }

Call ``atdts`` to produce ``hello.ts``::

  $ atdts hello.atd

There's now a file ``hello.ts`` that contains a class looking like
this:

.. code-block:: typescript

  ...

  export type Message = {
    subject: string;
    body: string;
  }

  export function writeMessage(x: Message, context: any = x): any {
    ...
  }

  export function readMessage(x: any, context: any = x): Message {
    ...
  }

  ...

Let's write a TypeScript program ``say_hello.ts`` that uses this code:

.. code-block:: typescript

  import * as hello from "./hello"

  const msg: hello.Message = {
    subject: "Hello",
    body: "Dear friend, I hope you are well."
  }

  console.log(JSON.stringify(hello.writeMessage(msg)))

Running it will print the JSON message::

  $ tsc --lib es2017,dom say_hello.ts
  {"subject":"Hello","body":"Dear friend, I hope you are well."}

Such JSON data can be parsed. Let's write a program
``read_message.ts`` that consumes JSON data from standard input:

.. code-block:: typescript

  import * as hello from "./hello"
  import * as readline from "readline"

  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  })

  rl.question('', (data: string) => {
    const msg = hello.readMessage(JSON.parse(data))
    console.log("subject: " + msg.subject)
  })


Output::

  # Install dependencies
  $ npm install --save-dev @types/node
  $ npm install readline

  # Compile
  $ tsc --lib es2017,dom read_message.ts

  # Run
  $ echo '{"subject": "big news", "body": ""}' | js read_message.js
  subject: big news

It works! But what happens if the JSON data lacks a ``"subject"``
field? Let's see::

  $ echo '{"body": ""}' | js read_message.js
  {"body": ""}
  readline.js:1086
              throw err;
              ^

  Error: missing field 'subject' in JSON object of type 'Message'
  ...

And what if our program also thought that the correct field name was
``subj`` rather than subject? Here's ``read_message_wrong.ts`` which
tries to access a ``subj`` field:

.. code-block:: typescript

  import * as hello from "./hello"
  import * as readline from "readline"

  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  })

  rl.question('', (data: string) => {
    const msg = hello.readMessage(JSON.parse(data))
    console.log("subject: " + msg.subj)
  })

Let's compile our program::

  $ tsc --lib es2017,dom read_message_wrong.ts
  read_message_wrong.ts:11:33 - error TS2339: Property 'subj' does not exist on type 'Message'.

  11   console.log("subject: " + msg.subj)
                                     ~~~~


  Found 1 error in read_message_wrong.ts:11

The typechecker detected that our program makes incorrect assumptions
about the message format without running it.

ATD Records, JSON objects, TypeScript objects
---------------------------------------------

An ATD file contains types that describe the structure of JSON
data. JSON objects map to TypeScript types and objects. They're called
records in the ATD language. Let's define a simple record type
in the file ``hello_plus.atd``:

.. code-block:: ocaml

   type message = {
     subject: string;
     ~body: string;
   }

Note the `~` in front of the ``body`` field. It means that this field
has a default value. Whenever the JSON field is missing from a JSON
object, a default value is assumed. The implicit default value for a
string is ``""``.

Let's add a ``signature`` field whose default value isn't the empty
string:

.. code-block:: ocaml

   type message = {
     subject: string;
     ~body: string;
     ~signature <ts default="'anonymous'">: string;
   }

Finally, we'll add an optional ``url`` field that doesn't take a default value
at all:

.. code-block:: ocaml

   type message = {
     subject: string;
     ~body: string;
     ~signature <ts default="'anonymous'">: string;
     ?url: string option;
   }

Let's generate the TypeScript code for this.

::

   $ atdts hello_plus.atd

Let's update our reader program ``read_message_plus.ts`` to this:

.. code-block:: typescript

  import * as hello_plus from "./hello_plus"
  import * as readline from "readline"

  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  })

  rl.question('', (data: string) => {
    const msg = hello_plus.readMessage(JSON.parse(data))
    console.log(msg)
  })

We can test it, showing us the final value of each field::

  $ tsc --lib es2017,dom read_message_plus.ts
  $ echo '{"subject":"hi"}' | js read_message_plus.js
  {"subject":"hi"}
  { subject: 'hi',
    body: '',
    signature: 'anonymous',
    url: undefined }

How-to guides
=============

..
  How-to guides are goal-oriented. They're for solving specific
  problems once the reader is a user with a sense of what the tool
  can do for them.
  documentation category: practical/producing

Defining default field values
-----------------------------

[missing]

Renaming field names
--------------------

[missing]


Deep dives
==========

..
  Deep dives are focused on understanding. They're discussions on a
  topic.
  documentation category: theoretical/exploring

[missing]

Reference
=========

..
  A reference is precise and complete.
  documentation category: theoretical/producing

Type mapping
------------

+--------------------+-------------------------------+-------------------------+
| ATD type           | TypeScript type               | JSON example            |
+====================+===============================+=========================+
| ``unit``           | ``null``                      | ``null``                |
+--------------------+-------------------------------+-------------------------+
| ``bool``           | ``bool``                      | ``True``                |
+--------------------+-------------------------------+-------------------------+
| ``int``            | ``Int``\*                     | ``42`` or ``42.0``      |
+--------------------+-------------------------------+-------------------------+
| ``float``          | ``number``                    | ``6.28``                |
+--------------------+-------------------------------+-------------------------+
| ``string``         | ``string``                    | ``"Hello"``             |
+--------------------+-------------------------------+-------------------------+
| ``string list``    | ``string[]``                  | ``["a", "b", "c!"]``    |
+--------------------+-------------------------------+-------------------------+
| ``(bool * float)`` | ``[boolean, number]``         | ``[-1, 1]``             |
+--------------------+-------------------------------+-------------------------+
| ``int nullable``   | ``Int | null``                | ``42`` or ``null``      |
+--------------------+-------------------------------+-------------------------+
| ``abstract``       | ``any``                       | anything                |
+--------------------+-------------------------------+-------------------------+
| ``{ id: string }`` | ``{ id: string }``            | ``{"id": "3hj8d"}``     |
+--------------------+-------------------------------+-------------------------+
| ``[A | B of int]`` | ``{kind: 'A'}``               | ``"A"`` or ``["B", 5]`` |
|                    | ``| {kind: 'B', value: Int}`` |                         |
+--------------------+-------------------------------+-------------------------+
| ``foo_bar``        | ``FooBar``                    |                         |
+--------------------+-------------------------------+-------------------------+

\*the ``Int`` type is an alias for ``number`` but additionally, the
read and write functions generated by atdts check that the number
is a whole number.

Supported ATD annotations
-------------------------

Default field values
^^^^^^^^^^^^^^^^^^^^

Record fields following a ``~`` assume a default value. The default value can
be implicit as mandated by the ATD language specification (false for
``bool``, zero for ``int``, etc.) or it can be a user-provided value.

A user-provided default uses an annotation of the form
``<ts default="VALUE">`` where ``VALUE`` evaluates to a TypeScript
expression e.g.

.. code-block:: ocaml

  type foo = {
    ~answer <ts default="42">: int;
  }

For example, the JSON value ``{}`` will be read as ``{answer: 42}``.

Field and constructor renaming
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Alternate JSON object field names can be specified using an annotation
of the form ``<json name="NAME">`` where ``NAME`` is the desired field
name to be used in the JSON representation. For example, the following
specifies the JSON name of the ``id`` field is ``ID``:

.. code-block:: ocaml

   type foo = {
     id <json name="ID">: string
   }

Similarly, the constructor names of sum types can also be given
alternate names in the JSON representation. Here's an example:

.. code-block:: ocaml

   type bar = [
   | Alpha <json name="alpha">
   | Beta <json name="beta"> of int
   ]


Alternate representations for association lists
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

List of pairs can be represented by JSON objects or by
TypeScript maps if the correct annotations are provided:

* ``(string * bar) list <json repr="object">`` will use JSON objects to
  represent a list of pairs of TypeScript type ``[string, Bar][]``.
  Using the annotation ``<json repr="array">`` is equivalent to the default.
* ``(foo * bar) list <ts repr="map">`` will use a TypeScript
  map of type ``Map<Foo, Bar>`` to represent the association list.
  Using the annotation ``<ts repr="array">`` is equivalent to the default.

Caveats
=========
* Generated typescript contains a flag telling the compiler not to run
  checks on the file. `(Read) <https://github.com/ahrefs/atd/issues/347>`_