How would you structure TypeScript code and exports for effective tree-shaking?
Tests ES module semantics and sideEffects for library authors. Great answers: emit ESM, set sideEffects:false, avoid stateful barrel files, and preserve import syntax. Red flag: saying ESM alone guarantees tree-shaking without mentioning side effects.
WHAT THIS TESTS: This question tests whether you understand the difference between writing application code and authoring a distributable library. Interviewers want to know if you grasp that bundlers like webpack rely on static analysis to eliminate dead code, and that TypeScript compilation settings and package metadata directly determine whether that analysis succeeds or fails. It separates candidates who merely consume libraries from those who understand module graph semantics.
A GOOD ANSWER COVERS: A strong answer hits four things in order. First, configure TypeScript to emit ES modules by setting module to esnext or nodenext and moduleResolution to bundler or node, which preserves static import and export syntax instead of transforming them into CommonJS require and module.exports. Second, declare sideEffects accurately in package.json, typically sideEffects false for pure utility libraries or a precise array pointing to CSS files when styles are involved, because webpack uses this field to decide whether skipping an unused file is safe. Third, avoid stateful re-exports and barrel files that execute code during import, such as initializing a singleton or mutating a prototype, since those create side effects that force bundlers to retain the entire module. Fourth, point the package exports or module field to the ESM build and avoid shipping CommonJS as the only format, because CommonJS dynamic require calls cannot be statically analyzed for tree-shaking.
COMMON WRONG ANSWERS: The biggest red flag is claiming that simply using export const makes a library tree-shakeable. Another mistake is setting sideEffects false when the library actually contains side effects like polyfills or global CSS imports, which breaks consumer builds. Recommending tsconfig module CommonJS is also wrong because it emits require syntax that bundlers cannot statically prune. Finally, ignoring nested dependencies is a pitfall; if your library depends on a non-tree-shakeable package, your consumers inherit that bloat.
LIKELY FOLLOW-UPS: An interviewer might ask how you would handle CSS imports in a component library while keeping sideEffects accurate, or how you verify tree-shaking actually works before publishing. They may also probe the difference between webpack sideEffects flag and pure module annotations in minifiers like Terser, or ask how conditional exports in package.json can serve both ESM and CJS consumers without breaking tree-shaking.
ONE CONCRETE EXAMPLE: Imagine a date-utils library with format, parse, and addDays functions. In tsconfig.json you set module to esnext and outDir to dist. In package.json you set type to module, main to dist/index.js, and sideEffects to false. Each function lives in its own file and is re-exported from index.ts using named exports. A consumer imports only format, so webpack sees the static import, checks sideEffects is false, and drops parse and addDays entirely. If you had instead compiled to CommonJS or forgotten sideEffects, the bundler would retain all three functions.
Source: webpack.js.org
Read the original → webpack.js.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.