An IIFE (immediately invoked function expression) is a function that is defined and executed in the same statement. The pattern has been part of JavaScript since the language’s early use in the browser, where it was a practical way to create a local scope and avoid polluting the global object when true modules were not available.

Basic shape

The function is wrapped in parentheses so the parser treats it as an expression, then it is called with ():

(function () {
  const secret = 42
  // `secret` is not visible outside this function
})()

With an arrow function, a similar effect is common in modern code:

;(() => {
  // one-off setup or isolated block
})()

A leading semicolon (or a line break before () avoids the rare case where the previous line’s expression is parsed as a function call.

Why it still matters in TypeScript

  1. Encapsulationconst and let inside the IIFE are not hoisted to an outer scope, so you can run initialization logic without exporting implementation details. With ES modules, file scope already provides privacy for top-level declarations, so IIFEs are less about “hiding from globals” and more about grouping one-time work or avoiding name clashes in a single file with many top-level symbols.

  2. Async IIFEasync functions return a Promise. Invoking one immediately is a standard way to use await at the top level inside a context that does not allow top-level await (older bundlers or scripts), or to fire-and-forget async setup while keeping the rest of the module synchronous:

    ;(async () => {
      await someSetup()
    })()

    In modern ES modules with top-level await enabled, you can often replace this with plain await at module scope.

  3. Return values — An IIFE can produce a value that you assign or export:

    export const config = (() => {
      const raw = process.env["FEATURE"]
      return raw === "on"
    })()

Trade-offs

IIFEs add nesting and can make stack traces slightly noisier. Prefer small named functions or plain module-level code when there is no need for an extra scope. Use an IIFE when you want a single expression that computes a value with private locals, or when you need async execution without making the whole module async.

References