Join the discussion

Write your take first — we'll ask for email only when you're ready to publish.

  • Hacker News
  • Would call stack/source map reasonably work with this?
  • Reasonably? Yes. Could probably be made to work well.

      $ bat 2.ts
      ───────────────
         1 │ function one() {
         2 │   let x = 1;
         3 │   defer (() => { throw new Error("thrown from one!"); })()
         4 │   x = 2;
         5 │ }
         6 │ one();
      ─────┴─────────
    
      $ node 2.js
      ~/healeycodes-typescript-go/2.js:29
                throw _errors_1[0];
                ^
    
      Error: thrown from one!
          at _callee_1 (/Users/andrew/Documents/GitHub/healeycodes-typescript-go/2.js:9:46)
          at /Users/andrew/Documents/GitHub/healeycodes-typescript-go/2.js:10:33
          at one (/Users/andrew/Documents/GitHub/healeycodes-typescript-go/2.js:22:31)
          at Object.<anonymous> (/Users/andrew/Documents/GitHub/healeycodes-typescript-go/2.js:34:1)
          at Module._compile (node:internal/modules/cjs/loader:1830:14)
          at Object..js (node:internal/modules/cjs/loader:1961:10)
          at Module.load (node:internal/modules/cjs/loader:1553:32)
          at Module._load (node:internal/modules/cjs/loader:1355:12)
          at wrapModuleLoad (node:internal/modules/cjs/loader:255:19)
          at Module.executeUserEntryPoint [as runMain (node:internal/modules/run_main:154:5)
    
      Node.js v24.15.0
  • I built a library to do something similar: https://www.npmjs.com/package/yaplib

    defer is the thing I miss the most coming from Go

  • Cool! I kinda feel like these should be built-in. But this looks useful for closing the DX gap here.
  • Is "defer" a good pattern? Aren't constructors/destructor pairs, like in C++/Rust, or "with" blocks in Python, better, because you cannot forget to call the destructor. I the first code example in the article, it is easy to omit "defer" and the compiler won't notice.
  • Defer is easier to fit into TS/JS as GC isn’t observable ( it kinda is with FinalisationRegistry but with a huge list of gotchas ).

    Defer only cares if the variable leaves scope. At the end they mention the new “using” syntax which only requires you to mark the declaration, not requiring an additional statement for the cleanup. It’s kinda nice but the resource needs to implement a cleanup method making it less flexible than defer.

  • Article describes try/finally as a hack to get the effect of defer, but it looks to me like it's the other way around? Try/finally is more traditional, in one form or another.
  • Author here. I find try/finally verbose and vulnerable to mistakes. For me, it adds "function noise".
  • Finally isn't bullet proof. If you execute a promise in a try block and it happens to end without resolving, the finally block will be never be executed. Fun stuff.
  • A (lexically-scoped) defer is a more general than a finally block. You can express finally-block semantics using defer.

    It's also more exception-safe when you have more than one throwing call in the try block.

  • If the idea of computers is that they remember stuff for you and do stuff for you, then both try/finally and defer seem like hacks to work around not having RAII like e.g. Rust or C++ where resources are closed/disposed of/deallocated automatically.

    Try X finally dispose of all resources. X, defer clean up X. Or how about just X and cleanup is done automatically, with the author of the resources deciding what is needed to clean X up.

  • It's also necessary here because TypeScript has exceptions and you'd expect your `defer`s to execute even when an exception is thrown.