Skip to main content

TypeScript API

The purpose of this document is describing the TypeScript API generated for Morphir models by running morphir-elm gen --target=TypeScript.

Generating TypeScript​

Given a model represented in morphir-ir.json, you can generate a TypeScript representation by running:

morphir-elm gen --input morphir-ir.json --output ./generated --target=TypeScript

Note that at present only the types are converted. Data values and functions are not.

You can generate a TypeScript representation of the Morphir IR itself by running this in the morphir-elm repo:

morphir-elm make ./morphir-make --types-only
morphir-elm gen --input=morphir-ir.json --output=./generated --target=TypeScript

Using the generated types​

The TypeScript backend outputs a top-level module per package, which your own code should import. The namespaces correspond with the package and module names in the IR. Only namespaces and symbols marked as public will be exported in the TypeScript API.

For example, you can use the IR types from the Morphir package like this:

import { Morphir } from './generated/Morphir'

const myName: Morphir.IR.Name.Name = ["this", "is", "a", "great", "name"]

Internally the types map to TypeScript type definitions. This is how a Morphir IR Name would be represented in generated/morphir/ir/Name.ts:

export type Name = Array<string>

You benefit from all the usual TypeScript type checking. For example, a Path must be a list of Name instances, so this example will raise an error:

import { Morphir } from './generated/Morphir'

const myName: Morphir.IR.Path.Path = "This is the wrong type."

You should see this message when compiling:

test.ts:3:7 - error TS2322: Type 'string' is not assignable to type 'Path'.

Most Morphir types correspond directly to JavaScript types. The JSON mapping gives a useful reference. There are some special cases, which are documented below.

Type mapping details​

Dict​

A Morphir.SDK.Dict.Dict K V maps to a TypeScript Map<K,V>.

Custom types​

We follow the example "Tagged Union Types in TypeScript" to implement custom types.

Each type variant is a TypeScript interface, with a kind and maybe some fields. The fields names are defined in the IR, and if you used morphir-elm make to build the IR then the names will follow the pattern arg1, arg2, arg3 and so on.

Constructor functions are provided for these. Here's an example using the Morphir IR Value custom type, creating an instance of its Reference variant:

import { Morphir } from './generated/Morphir'

const exampleFQName: Morphir.IR.FQName.FQName = [[], [[]], ["excellent", "name"]];

type AttrType = [];
let myReference = new Morphir.IR.Value.Reference<AttrType>([], exampleFQName);

Calling the constructor function is equivalent to manually constructing an object and setting the relevant properties:

let myReference: Morphir.IR.Value.Reference<AttrType> = {
kind: "Reference",
arg1: [],
arg2: exampleFQName,
}

Constructor functions are only provided for custom types.

Type variables​

Morphir's custom types and type aliases can use type variables. These map to TypeScript generics.

Here's an example using Morphir IR's AccessControlled type, which is a type alias that maps to a Record.

import { Morphir } from './generated/Morphir'

const myAccess = new Morphir.IR.AccessControlled.Public();

let myAccessControlled: Morphir.IR.AccessControlled.AccessControlled<String> = {
access: myAccess,
value: "I'm a string",
}

JSON serialization and deserialization​

The generated TypeScript API includes decode and encode functions for each type, used to serialize and deserialize instances of the types according to the standard Morphir JSON mapping.

With the generated Morphir.IR API, this allows you to read entire morphir-ir.json files into your TypeScript program and create instances of the appropriate types. Here's how you might do that:

import { Morphir } from './generated/Morphir'

function loadMorphirIR(text) {
let data = JSON.parse(text);

if (data['formatVersion'] != 2) {
throw "Unsupported morphir-ir.json format";
}

return Morphir.IR.Distribution.decodeDistribution(data['distribution']);
}