Skip to content
Bruno Jouhier edited this page Aug 25, 2017 · 22 revisions

TypeScript support has been added in 2.0.10. It allows you to take advantage of TypeScript's type annotations and related tooling (vscode is awesome 👍) with streamline code.

Enabling streamline in a TypeScript project

To enable streamline in a TypeScript project, just run:

npm install --save streamline-runtime
npm install --save-dev streamline-typings

The streamline-runtime library is needed at runtime. The streamline-typings package contains ambient type definitions for the tsc compiler. You should only install it in development mode.

Streamline does not introduce a different file extension because vscode only supports type annotations in .ts files. Instead, it uses the presence of a streamline-runtime dependency in the project's package.json to decide if the .ts files should go through streamline.js transpilation or not.

At least one of your project source files must contains the following directive, to activate the streamline-typings definitions.

/// <reference path="../node_modules/streamline-typings/streamline-runtime.d.ts" />

Compiling files

A first option is to compile files with _node, as usual. There is no special command line option, just the package.json auto-detection described above.

A second option is to use a loader and node.js require hooks. Files will be compiled on the fly, when required. This is achieved, as usual, with a call to the register API:

require("streamline").register({});

Special APIs and types

Streamline needs a special type for the magic _ callback marker. Naturally, this special type is also called _. You import it with:

import { _ } from 'streamline-runtime';

And then you use it to type your functions and methods:

function archiveOrders(date: Date, _: _) {
  ...
}

_ is also the entry door to a small library of runtime support functions. This library covers the API that was previously exposed in the flows and globals runtime module, and it includes a few helpers for interop with callback and promise APIs. See details below.

Interop with Promise APIs

You can consume any Promise API with promise.then(_, _), as before. Of course, the returned value will be properly typed. For example, you can call the mongodb API as follows:

var myOrders = db.collection('Orders')
  .find({ date: { $gt: d } })
  .toArray()
  .then(_, _);

In the other direction, you can obtain a promise from any streamline function with _.promise:

var promise = _.promise(_ => archiveOrders(date, _));

// later:
promise.then(orders => { ... }, error => { ... });

Promises are becoming the de-facto standard to interoperate with 3rd-party code, as they are now part of the language, and spreading into the ecosystem.

Interop with node.js callbacks

Some libraries don't expose a Promise API, only a callback API; the main one being node.js itself. You have several options to use them with streamline:

The first one is to use ambient type definitions. You do not need to write any special wrapper, just add an ambient type definitions file to your typings directory and you can start using the API as usual.

For example, you can get ambient type definitions for the whole node.js API by installing streamline-node, and then use node.js APIs as usual:

import * as fs from 'fs';

export function countLines(filename: string, _: _) {
  return fs.readFile(filename, 'utf8', _).split('\n').length;
}

If you do not have ambient definitions for your APIs you should create one. There is no risk as you won't need to change anything in your implementation.

If you really want to do without ambient definitions, you can cast to any and call the function directly:

// assumes you did not install streamline-node.d.ts (why wouldn't you?)
import * as fs from 'fs';

export function countLines(filename: string, _: _) {
  // heavier and a bit brittle, but works
  const text: string = fs.readFile(filename, 'utf8', _ as any) as any;
  return text.split('\n').length;
}

You can also expose your streamline functions as callback-based functions. Usually these functions will be consumed by regular .js modules. So you don't need to anything special as streamline functions are actually transpiled to regular node.js callback APIs. For example, the above countLines function can be consumed as:

// assuming countLines was defined in myfs.ts, I can consume it in JavaScript as:
var myfs = require('./myfs');

myfs.countLines(__filename, (count, err) => {
  if (err) throw err;
  console.log(`${__filename}: ${count} lines`);
});

Futures

Streamline's futures (aka. thunks) will probably become less and less attractive, as promises are gaining more and more traction. But you can still use them. The !_ syntax works but does not typecheck. Instead, you should use the _.future API.

To create a future:

var fut = _.future(_ => archiveOrders(date, _));

Then you can await its result as usual:

var result = fut(_);

Global context

The global context is also exposed via the _ token:

// setting state into the context
_.context.myState = ...;

// retrieving it elsewhere
var myState = _.context.myState;

Flows API

The flows API is also exposed by the _ token. For example:

const fun = _.funnel(1);

function computeExclusive(_, x) {
  return fun(_, _ => compute(_, x);
}