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
-
Encapsulation —
constandletinside 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. -
Async IIFE —
asyncfunctions return aPromise. Invoking one immediately is a standard way to useawaitat the top level inside a context that does not allow top-levelawait(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
awaitat module scope. -
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.