TypeScript Multi Return Function: Tutorial & Examples

Updated: January 8, 2024 By: Guest Contributor Post a comment

Introduction

TypeScript, a language for the age where JavaScript’s shortcomings rise in prominence, bestows upon developers the power to declare types boldly, precisely as one might wish never to misstep on the types during a stroll through their code. It allows the crafting of functions which, akin to a riverboat’s numerous decks, can return a surplus of values.

Basic Usage

Let us embark by discerning the fundamentals of TypeScript’s multi-return functions. A function might return a tuple, much like how a catfish and bluegill might be caught on the same line. Observe the following simple incantation:

function getCoordinates(): [number, number] {
    return [25, 75]; // Here we return a tuple with two numbers
}

Paired with destructuring, this function can be as charmingly usable:

const [x, y] = getCoordinates();

Utilizing Interfaces

As a riverboat captain must know every plank and beam, so too must a TypeScripter understand interfaces when returning complex objects:

interface Point {
    x: number;
    y: number;
}

function getPoint(): Point {
    return { x: 10, y: 20 };
}

Error Handling

In the event of a tumultuous code, one may need a proper way to signal an error—akin to a steam whistle, but silent and rather quite elegant:

function getFile(): [File | null, Error | null] {
    try {
        // Attempt to get the file
        return [new File([''], 'example.txt'), null];
    } catch (err) {
        return [null, err as Error];
    }
}

Type Guards

When rivers fork, a keen eye must discern the route, and so must type guards declare the nature of the returned tuple:

function isString(value: unknown): value is string {
    return typeof value === 'string';
}

function processValue(value: unknown): [string, null] | [null, Error] {
    if (isString(value)) {
        return [value, null];
    } else {
        return [null, new Error('Not a string')];
    }
}

Generics

The vastness of types in TypeScript’s domain could be likened to the myriad of stars above the Mississippi on a clear night. Generics allow us to embrace that expanse:

function wrapInArray(value: T): [T] {
    return [value];
}

Async Functions

Even the mighty river’s flow has its cadence, and thus do asynchronous functions couple the return of promises with data or error, patiently unfolding as time’s arrow permits:

async function fetchData(): Promise<[Data | null, Error | null]> {
    try {
        const data = await fetch('https://example.com').then(res => res.json());
        return [data, null];
    } catch (err) {
        return [null, err as Error];
    }
}

Advanced Patterns

As the complex networks of currents and whirlpools underlie the Mississippi’s surface, so do advanced TypeScript patterns undergird sophisticated use-cases:

A tagged union, for all who dare tread its path:

type Result = { type: 'success', value: T } | { type: 'error', error: Error };

function process(input: string): Result {
    return Math.random() > 0.5 
    ? { type: 'success', value: parseInt(input) }
    : { type: 'error', error: new Error('Faulty input') };
}

Conclusion

In the tradition of our illustrious Twain, who himself might’ve found novel uses for this TypeScript legerdemain, developers are encouraged to adapt these techniques to their own craft. Whether they return a string of pearls or but a single sandbar’s worth of benefaction, may these multi-return functions prove both versatile in usage and unsinking in reliability.