TypeScript Utilities For Candid

A collection of functions to handle Nullable, Date and Blob when interacting with canister smart contracts.

Nov 16, 2021

#typescript #blockchain #motoko #javascript

Photo by Georgie Cobbs on Unsplash

To port our web editor, DeckDeckGo, to DFINITY’s Internet Computer I developed several helpers in TypeScript to interact with our canister smart contracts.

If it can make your life easier too, here are those I use the most.


The Candid description that is generated for nullable types does not exactly match what I commonly used in JavaScript for optional types (see this post for the why and how).

For example, if we generate an interface for such a Motoko code snippet:

actor Example { public shared query func list(filter: ?Text) : async [Text] { let results: [Text] = myFunction(filter); return results; }; }

The definition of the optional parameter filter will not be interpreted as a string that can potentially be undefined but, rather as a one-element length array that contains a string or is empty.

export interface _SERVICE { list: (arg_0: [] | [string]) => Promise<Array<string>>; }

That is why I created functions to convert back and forth optional values.

export const toNullable = <T>(value?: T): [] | [T] => { return value ? [value] : []; }; export const fromNullable = <T>(value: [] | [T]): T | undefined => { return value?.[0]; };

toNullable convert an object that can either be of type T or undefined to what’s expected to interact with the IC and, fromNullable do the opposite.


System Time (nanoseconds since 1970–01–01) gets parsed to bigint and exported as a type Time in Candid definition.

export type Time = bigint;

To convert JavaScript Date to big numbers, the built-in object BigInt can be instantiated by multiplying seconds to nano seconds.

export const toTimestamp = (value: Date): Time => { return BigInt(value.getTime() * 1000 * 1000); };

The other way around works by converting first the big numbers to their primitive Number types and dividing it to seconds.

export const fromTimestamp = (value: Time): Date => { return new Date(Number(value) / (1000 * 1000)); };

To support Nullable timestamps values, I also created the following helpers that extend above converters and return the appropriate optional arrays.

export const toNullableTimestamp = (value?: Date): [] | [Time] => { const time: number | undefined = value?.getTime(); return value && !isNaN(time) ? [toTimestamp(value)] : []; }; export const fromNullableTimestamp = (value?: [] | [Time]): Date | undefined => { return !isNaN(parseInt(`${value?.[0]}`)) ? fromTimestamp(value[0]) : undefined; };


Binary blobs are described in Candid as Array of numbers. To save untyped data in smart contracts (assuming the use case allows such risk) while still preserving types on the frontend side, we can stringify objects, converts these to blobs and gets their contents as binary data contained in an ArrayBuffer.

export const toArray = async <T>(data: T): Promise<Array<number>> => { const blob: Blob = new Blob([JSON.stringify(data)], {type: 'application/json; charset=utf-8'}); return [...new Uint8Array(await blob.arrayBuffer())]; };

To convert back an Array of numbers to a specific object type, the Blob type can be used again but, this time a textual conversion shall be used to parse the results.

export const fromArray = async <T>(data: Array<number>): Promise<T> => { const blob: Blob = new Blob([new Uint8Array(data)], {type: 'application/json; charset=utf-8'}); return JSON.parse(await blob.text()); };

Both conversions are asynchronous because interacting with the blob object requires resolving promises in JavaScript.

Further Reading

Wanna read more our project? Here is the list of blog posts I published since we started the project with the Internet Computer:

Keep In Touch

To follow our adventure, you can star and watch our GitHub repo ⭐️ and sign up to join the list of beta tester.


I hope this short blog post and few utilities will be useful for you to start well with the Internet Computer, it is really a fun technology.

To infinity and beyond!