forked from digital-asset/daml
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
daml2ts : Explain DAML/TypeScript mappings (digital-asset#4196)
changelog_begin changelog_end Co-authored-by: Shayne Fletcher <shayne.fletcher@digitalasset.com>
- Loading branch information
1 parent
60b0c4a
commit a56caa0
Showing
3 changed files
with
292 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,290 @@ | ||
.. Copyright (c) 2020 The DAML Authors. All rights reserved. | ||
.. SPDX-License-Identifier: Apache-2.0 | ||
TypeScript! | ||
########### | ||
|
||
daml2ts : TypeScript of DAML | ||
============================ | ||
|
||
Introduction | ||
------------ | ||
|
||
``daml2ts`` generates TypeScript for interacting with DAML via the `json-api <../json-api/index.html>`_ . | ||
|
||
``daml2ts`` inputs are compiled DAML modules. ``daml2ts`` outputs are TypeScript source files containing definitions modeling the DAML entities found. | ||
|
||
The code ``daml2ts`` generates is in terms of the package `@daml/types <https://github.com/digital-asset/daml/tree/master/language-support/ts/daml-types>`_. | ||
|
||
Primitives: @daml/types | ||
----------------------- | ||
|
||
To understand the TypeScript code generated by ``daml2ts``, it is helpful to keep in mind this quick review of the primitives provided by @daml/types. | ||
|
||
**Interfaces**: | ||
|
||
- ``Serializable<T>`` | ||
- ``Template<T extends object, K = unknown>`` | ||
- ``Choice<T extends object, C, R, K = unknown>`` | ||
|
||
.. | ||
An object that extends the ``Serializable<T>`` interface has a field ``decoder: () => jtv.Decoder<T>``. | ||
An object that extends the ``Template<T, K>`` interface has fields ``templateId: string``, ``keyDecoder: ()=> jtv.Decoder<K>``, ``Archive: Choice<T, {}, {}>`` and extends ``Serializable<T>``. | ||
An object that extends the ``Choice<T, C, R, K>`` interface has fields ``template: () => Template<T, K>``, ``argumentDecoder: () => jtv.Decoder<C>``, ``resultDecoder: () => jtv.Decoder<R>`` and ``choiceName: string``. | ||
**Types**: | ||
|
||
+-------------------+------------------+----------------------------------+ | ||
| TypeScript | DAML equiv. | TypeScript definition | | ||
+===================+==================+==================================+ | ||
| ``Unit`` | ``()`` | ``{}`` | | ||
+-------------------+------------------+----------------------------------+ | ||
| ``Bool`` | ``Bool`` | ``boolean`` | | ||
+-------------------+------------------+----------------------------------+ | ||
| ``Int`` | ``Int`` | ``string`` | | ||
+-------------------+------------------+----------------------------------+ | ||
| ``Decimal`` | ``Decimal`` | ``string`` | | ||
+-------------------+------------------+----------------------------------+ | ||
| ``Numeric`` | ``Numeric n`` | ``string`` | | ||
+-------------------+------------------+----------------------------------+ | ||
| ``Text`` | ``Text`` | ``string`` | | ||
+-------------------+------------------+----------------------------------+ | ||
| ``Time`` | ``Time`` | ``string`` | | ||
+-------------------+------------------+----------------------------------+ | ||
| ``Party`` | ``Party`` | ``string`` | | ||
+-------------------+------------------+----------------------------------+ | ||
| ``List<T>`` | ``[t]`` | ``T[]`` | | ||
+-------------------+------------------+----------------------------------+ | ||
| ``Date`` | ``Date`` | ``string`` | | ||
+-------------------+------------------+----------------------------------+ | ||
| ``ContractId<T>`` | ``ContractId t`` | ``string`` | | ||
+-------------------+------------------+----------------------------------+ | ||
| ``Optional<T>`` | ``Optional t`` | ``null | (null extends T ?`` | | ||
| | | ``[] | [Exclude<T, null>] : T)`` | | ||
+-------------------+------------------+----------------------------------+ | ||
| ``TextMap<T>`` | ``TextMap t`` | ``{ [key: string]: T }`` | | ||
+-------------------+------------------+----------------------------------+ | ||
|
||
.. note:: | ||
The TypeScript types ``Time``, ``Decimal``, ``Numeric`` and ``Int`` all alias to ``string``. These choices relate to the avoidance of precision loss under serialization over the `json-api <../json-api/index.html>`_. | ||
|
||
.. note:: | ||
The Typescript definition of type ``Optional<T>`` in the above table might look complicated. It accounts for differences in the encoding of optional values when nested versus when they are not (i.e. "top-level"). For example, ``null`` and ``"foo"`` are two possible values of ``Optional<Text>`` whereas, ``[]`` and ``["foo"]`` are two possible values of type ``Optional<Optional<Text>>`` (``null`` is another possible value, ``[null]`` is **not**). | ||
|
||
DAML to TypeScript mappings | ||
--------------------------- | ||
|
||
The mappings from DAML to TypeScript are best explained by example. | ||
|
||
Records | ||
~~~~~~~ | ||
|
||
In DAML, we might model a person like this. | ||
|
||
.. code-block:: daml | ||
:linenos: | ||
data Person = | ||
Person with | ||
name: Text | ||
party: Party | ||
age: Int | ||
Given the above definition, the generated TypeScript code will be as follows. | ||
|
||
.. code-block:: typescript | ||
:linenos: | ||
type Person = { | ||
name: string; | ||
party: daml.Party; | ||
age: daml.Int; | ||
} | ||
Tuples | ||
~~~~~~ | ||
|
||
Suppose the following DAML definition. | ||
|
||
.. code-block:: daml | ||
:linenos: | ||
data Pair a b = Pair (a, b) | ||
In TypeScript, the above definition renders as below [1]_. | ||
|
||
.. code-block:: typescript | ||
:linenos: | ||
type Pair<a, b> = | ||
| { tag: 'Pair'; value: {_1: a, _2: b} } | ||
.. [1] This is a simplification but it is not wrong to think of it this way. | ||
.. note:: | ||
The general rule is that DAML tuples are represented in TypeScript as simple objects with fields labeled ``_1``, ``_2`` and so on. | ||
|
||
The DAML type ``Pair a b`` has been modeled as a TypeScript discriminated union (with a single case). In general, all DAML variants are translated so as we will see in the next section. | ||
|
||
Variants | ||
~~~~~~~~ | ||
|
||
This is a DAML type for a language of additive expressions. | ||
|
||
.. code-block:: daml | ||
:linenos: | ||
data Expr a = | ||
Lit a | ||
| Var Text | ||
| Add (Expr a, Expr a) | ||
In TypeScript, it is represented as a discriminated union. | ||
|
||
.. code-block:: typescript | ||
:linenos: | ||
type Expr<a> = | ||
| { tag: 'Lit'; value: a } | ||
| { tag: 'Var'; value: string } | ||
| { tag: 'Add'; value: {_1: Expr<a>, _2: Expr<a>} } | ||
Sum-of-products | ||
~~~~~~~~~~~~~~~~ | ||
|
||
Let's slightly modify the ``Expr a`` type of the last section into the following. | ||
|
||
.. code-block:: daml | ||
:linenos: | ||
data Expr a = | ||
Lit a | ||
| Var Text | ||
| Add {lhs: Expr a, rhs: Expr a} | ||
Compared to the earlier definition, the ``Add`` case is now in terms of a record with fields ``lhs`` and ``rhs``. This renders in TypeScript like so. | ||
|
||
.. code-block:: typescript | ||
:linenos: | ||
type Expr<a> = | ||
| { tag: 'Lit2'; value: a } | ||
| { tag: 'Var2'; value: string } | ||
| { tag: 'Add'; value: Expr.Add<a> } | ||
namespace Expr { | ||
type Add<a> = { | ||
lhs: Expr<a>; | ||
rhs: Expr<a>; | ||
} | ||
} | ||
The thing to note is how the definition of the ``Add`` case has given rise to a record type definition ``Expr.Add``. | ||
|
||
Enums | ||
~~~~~ | ||
|
||
DAML enumerations map naturally to TypeScript. | ||
|
||
.. code-block:: daml | ||
:linenos: | ||
data Color = Red | Blue | Yellow | ||
The companion TypeScript type is the following. | ||
|
||
.. code-block:: typescript | ||
:linenos: | ||
enum Color { | ||
Red = 'Red', | ||
Blue = 'Blue', | ||
Yellow = 'Yellow', | ||
} | ||
Templates and choices | ||
~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
Here is a DAML template of a basic 'IOU' contract. | ||
|
||
.. code-block:: daml | ||
:linenos: | ||
template Iou | ||
with | ||
issuer: Party | ||
owner: Party | ||
currency: Text | ||
amount: Decimal | ||
where | ||
signatory issuer | ||
choice Transfer: ContractId Iou | ||
with | ||
newOwner: Party | ||
controller owner | ||
do | ||
create this with owner = newOwner | ||
``daml2ts`` generates types for each of the choices defined on the template as well as the template itself. | ||
|
||
.. code-block:: typescript | ||
:linenos: | ||
type Transfer = { | ||
newOwner: daml.Party; | ||
} | ||
type Iou = { | ||
issuer: daml.Party; | ||
owner: daml.Party; | ||
currency: string; | ||
amount: daml.Numeric; | ||
} | ||
Each template and each template choice also results in the generation of a companion object corresponding to the choice or template type. Here, is a schematic of the ones generated from the ``Iou`` template. | ||
|
||
.. code-block:: typescript | ||
:linenos: | ||
const Transfer: daml.Serializable<Transfer> = ({ | ||
/* ... */ | ||
}), | ||
}) | ||
const Iou: daml.Template<Iou, undefined> & { | ||
Archive: daml.Choice<Iou, DA_Internal_Template.Archive, {}, undefined>; | ||
Transfer: daml.Choice<Iou, Transfer, daml.ContractId<Iou>, undefined>; | ||
} = { | ||
/* ... */ | ||
} | ||
The exact details of these companion objects are not important - think of them as representing "metadata". | ||
|
||
What **is** important is the use of the companion objects when creating contracts and exercising choices using the `@daml/ledger <https://github.com/digital-asset/daml/tree/master/language-support/ts/daml-ledger>`_ package. The following code snippet demonstrates their usage. | ||
|
||
.. code-block:: typescript | ||
:linenos: | ||
import Ledger from '@daml/ledger'; | ||
const ledger = new Ledger(/* ... */); | ||
// Contract creation; Bank issues Alice a USD $1MM IOU. | ||
const iouDetails: Iou = { | ||
issuer: 'Chase', | ||
owner: 'Alice', | ||
currency: 'USD', | ||
amount: 1000000.0, | ||
}; | ||
const aliceIouCreateEvent = await ledger.create(Iou, iouDetails); | ||
const aliceIouContractId = aliceIouCreateEvent.contractId; | ||
// Choice execution; Alice transfers ownership of the IOU to Bob. | ||
const transferDetails: Transfer = { | ||
newOwner: 'Bob', | ||
} | ||
const [bobIouContractId, _] = await ledger.exercise(Transfer, aliceIouContractId, transferDetails); | ||
Observe on line *12*, the first argument to ``create`` is the ``Iou`` companion object and on line *20*, the first argument to ``exercise`` is the ``Transfer`` companion object. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters