Defer in TypeScript

In TypeScript, there isn’t a direct equivalent to the defer keyword. However, we can achieve similar functionality using the try...finally block or by implementing a custom defer mechanism. Let’s look at both approaches:

  1. Using try...finally:
import * as fs from 'fs';

function main() {
    let file: fs.WriteStream | null = null;
    try {
        file = createFile("/tmp/defer.txt");
        writeFile(file);
    } finally {
        if (file) {
            closeFile(file);
        }
    }
}

function createFile(p: string): fs.WriteStream {
    console.log("creating");
    return fs.createWriteStream(p);
}

function writeFile(f: fs.WriteStream) {
    console.log("writing");
    f.write("data\n");
}

function closeFile(f: fs.WriteStream) {
    console.log("closing");
    f.close((err) => {
        if (err) {
            console.error(`error: ${err}`);
            process.exit(1);
        }
    });
}

main();
  1. Implementing a custom defer mechanism:
import * as fs from 'fs';

class Defer {
    private actions: (() => void)[] = [];

    add(fn: () => void) {
        this.actions.push(fn);
    }

    run() {
        while (this.actions.length) {
            const action = this.actions.pop();
            if (action) action();
        }
    }
}

function main() {
    const defer = new Defer();
    const file = createFile("/tmp/defer.txt");
    defer.add(() => closeFile(file));
    writeFile(file);
    defer.run();
}

function createFile(p: string): fs.WriteStream {
    console.log("creating");
    return fs.createWriteStream(p);
}

function writeFile(f: fs.WriteStream) {
    console.log("writing");
    f.write("data\n");
}

function closeFile(f: fs.WriteStream) {
    console.log("closing");
    f.close((err) => {
        if (err) {
            console.error(`error: ${err}`);
            process.exit(1);
        }
    });
}

main();

In both examples, we’re using the fs module to work with files. The createFile function creates a write stream, writeFile writes to the stream, and closeFile closes the stream.

In the first approach, we use a try...finally block to ensure that the file is closed even if an error occurs during writing.

In the second approach, we implement a custom Defer class that mimics the behavior of the defer keyword. Actions are added to the Defer instance and executed in reverse order when run() is called.

To run the program:

$ ts-node defer.ts
creating
writing
closing

This confirms that the file is closed after being written, similar to the original example.

Note that TypeScript doesn’t have a direct equivalent to panic, so error handling is done through exceptions or by passing errors to callbacks, as is common in Node.js.