59 build and typecheck review
Skill FluxonLab/Skillry/plugins/devops-and-release/skills/59-build-and-typecheck-review
Use when you need to review build, typecheck, compile, bundling, and static validation commands.From its SKILL.md
npx -y skills add FluxonLab/Skillry --skill 59-build-and-typecheck-reviewAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
11.2 KB, ~2.8k tokens by cl100k_base, as published. Nobody here has run it
Build and Typecheck Review
Purpose
Verify the project builds cleanly with strict static analysis, catch suppressed or ignored TypeScript errors and compiler warnings, review tsconfig.json strictness, assess bundle size and tree-shaking, confirm source maps are generated correctly, and identify build-tooling anti-patterns that cause silent failures or non-reproducible builds. The review runs read-only static checks (tsc --noEmit, grep) and never emits build output or mutates config without instruction.
When to use
tsc --noEmitis failing in CI and you need to triage and fix the errors.- A PR introduces
// @ts-ignoreor@ts-expect-errorwithout justification. tsconfig.jsonwas modified and you want to confirm strict mode is preserved.- Bundle size has grown unexpectedly and you need to identify the cause.
- The build passes locally but fails in CI (environment-dependent build issues).
- A new package is added and may not be tree-shakeable.
When not to use
- The project is JavaScript only (no TypeScript) — skip the typecheck steps.
- The task is to implement a feature, not to review the build configuration.
- Build issues are in a Docker layer or deployment step — use
60-deployment-preflight-review.
Procedure
- Run
tsc --noEmitand capture output. Type-check without emitting files for the full error list. For project references, runtsc --build --noEmit. Count errors by file and by error code. - Review
tsconfig.jsonstrict settings. Check"strict": true; if absent, checknoImplicitAny,strictNullChecks,strictFunctionTypes,noUncheckedIndexedAccess. NoteskipLibCheck: trueif it hides errors in.d.tsfiles the project owns. ConfirmnoEmitOnError: true. - Audit
@ts-ignore/@ts-expect-error. For each: is there a comment explaining why? Is it hiding a real error that should be fixed? Is@ts-expect-errorused (preferred — fails if the error disappears)? - Check
anyusage. Flag explicitanyin non-test code, especially in function signatures and return types — it is a type-safety hole. - Verify build reproducibility. Confirm the output hash is stable across two consecutive clean builds. Instability suggests timestamp injection, random IDs, or non-deterministic module ordering.
- Review bundle size and tree-shaking. Check
sideEffects: falsefor library packages; dynamicimport()for large sections; whole-library imports (import _ from 'lodash') that should be path imports. - Check source map configuration. Production maps should be external (
devtool: 'source-map'), not inline; not served publicly; uploaded to an error tracker. - Verify build command consistency.
package.jsonscripts, the CI workflow, and the Dockerfile must call the same build command. - Check warnings treated as errors.
--noUnusedLocalsand--noUnusedParametersenabled; bundler warnings (circular dependency, missing exports) surfaced, not suppressed. - Review path aliases.
tsconfig.jsonpathsmust match the bundler config and JestmoduleNameMapper; mismatches cause "module not found" in only one context.
Concrete checks
-
tsc --noEmitexits with zero errors. -
"strict": true(or equivalent individual flags) is set intsconfig.json. -
"noEmitOnError": trueis set. - All
@ts-ignoreusages have an explanatory comment; none suppress real correctness bugs. -
as anyand: anyusages in non-test code are minimal and justified. - Build output is reproducible (same hash on two consecutive clean builds).
- Bundle size is within defined thresholds (or thresholds are defined if missing).
- Dynamic imports are used for large page sections.
- Source maps are external, not inline, in production builds.
- Path aliases are consistent across
tsconfig.json, bundler config, and Jest config. -
--noUnusedLocalsand--noUnusedParametersare enabled.
Commands or Templates
# Type gate: full error list, no emit (use --build for project references)
npx tsc --noEmit 2>&1 | tee /tmp/tsc.txt
grep -oE "error TS[0-9]+" /tmp/tsc.txt | sort | uniq -c | sort -rn # errors by code
# Strictness flags actually in effect
npx tsc --showConfig | grep -E '"strict"|"noImplicitAny"|"strictNullChecks"|"noEmitOnError"|"noUnusedLocals"'
# Suppressions and any-holes in non-test code
grep -rn "@ts-ignore\|@ts-expect-error" src/
grep -rnE ":\s*any\b|as any|as unknown as " src/ --include="*.ts" --include="*.tsx"
# Whole-library imports that defeat tree-shaking
grep -rnE "import .* from ['\"](lodash|moment|rxjs)['\"]" src/
# Reproducibility: hash output twice from clean
rm -rf dist && npm run build >/dev/null 2>&1 && find dist -type f -exec sha256sum {} + | sort > /tmp/b1
rm -rf dist && npm run build >/dev/null 2>&1 && find dist -type f -exec sha256sum {} + | sort > /tmp/b2
diff /tmp/b1 /tmp/b2 && echo "reproducible" || echo "NON-REPRODUCIBLE"
tsconfig strictness reference
"strict": true is the umbrella; when it is off, audit these individually because each closes a real class of bug:
| Flag | What it catches | Risk if off |
|---|---|---|
noImplicitAny | params/vars with no inferable type | silent any everywhere |
strictNullChecks | undefined/null not in the type | Cannot read properties of undefined at runtime |
strictFunctionTypes | unsound callback parameter variance | wrong-shaped callbacks accepted |
noUncheckedIndexedAccess | arr[i] assumed defined | out-of-bounds reads typed as present |
noEmitOnError | emitting JS despite type errors | broken build artifacts ship |
noUnusedLocals/Parameters | dead bindings | code rot accumulates |
skipLibCheck: true is acceptable for third-party .d.ts noise, but if the project generates its own declaration files, it also silences their errors — note that explicitly.
Worked error triage
tsc --noEmit reports 38 errors. Grouping by code (the command in the templates) shows: 31× TS2532 Object is possibly 'undefined', 5× TS2345, 2× TS7006. The 31 TS2532 errors all trace to one source: strictNullChecks was just enabled and array/map lookups are no longer assumed defined. The fix is not 31 as casts — it is a few guard patterns at the lookup sites (const u = users.get(id); if (!u) return;). Triaging by error code rather than by file reveals that most of the count is a single root cause, so the report should say "31 errors, one cause (missing null guards after enabling strictNullChecks), ~6 guard sites to fix" — not "38 unrelated errors".
Common issues & anti-patterns
strict: falseinherited from a template. Teams disable strict mode to suppress initial errors and never re-enable it; type safety degrades.skipLibCheck: truehiding owned.d.tserrors. Errors in generated declaration files the project ships are also skipped.- Build succeeds but emits with errors.
noEmitOnErroris false; the build produces broken JS from files with type errors. as unknown as Foocast chains. Defeat the type checker entirely — equivalent toanywithout the grep signal forany.- Full lodash import.
import _ from 'lodash'pulls ~73KB minified;import debounce from 'lodash/debounce'pulls ~2KB. - Date-stamped bundle filename without content hash.
bundle.2026-05-31.jsinvalidates CDN cache by date, not content; usebundle.[contenthash].js. - Mismatched
targetandlib.target: "es5"withlib: ["esnext"]emits ES5 that calls ES2022 methods absent at runtime. - Circular dependency. Bundles work until an edge case triggers an undefined reference at runtime; surface the warning.
- Incremental build cache hiding errors. A stale
tsBuildInfoor bundler cache can report success on code that no longer compiles clean; verify from a clean state when in doubt. transpileOnly/ Babel type-stripping treated as a typecheck.ts-node --transpileOnly,esbuild, andswcstrip types without checking them; the only real type gate istsc --noEmit. A green dev server says nothing about type correctness.
Verify the typecheck is real
A frequent false signal is "the app runs, so types are fine". Dev servers and bundlers transpile by stripping types, not checking them — so type errors pass straight through to runtime. Always run a dedicated tsc --noEmit (or tsc --build --noEmit for references) as the gate, separate from the build/run step, and confirm it is wired into CI. If the project's "typecheck" script secretly runs the bundler, it is not a type gate; flag that as a finding.
"Passes locally, fails in CI"
This recurring class almost always traces to an environment difference, not a real code change. Check, in order:
- Filesystem case sensitivity. macOS/Windows are case-insensitive; Linux CI is not.
import './Utils'for a file namedutils.tspasses locally and fails in CI with "module not found". Grep imports against actual filenames. - Dependency drift. Local
node_moduleswas built from a stale lockfile; CI does a cleannpm ci. A version that exists locally but not in the lockfile resolves differently. Reproduce with a clean install. tsconfigresolution differences. Apathsalias resolved by the IDE/ts-nodelocally but absent from the bundler ortscbuild config used in CI.- Node/TypeScript version mismatch. Local Node 22 vs CI Node 18; a syntax or
libfeature differs. Confirmenginesand the CI matrix agree. - Case-only env differences. A type that depends on
process.env.NODE_ENVbeing set narrows differently when the var is unset in CI.
The fix is to make the local environment match CI (clean install, same Node, Linux-equivalent case behavior) and reproduce the failure — not to add a suppression so the local build stays green while CI stays red.
Required output
## Build and Typecheck Review
### TypeScript errors
- Total: N | Top files: path — N | Codes by frequency: TS2345 (N), TS2322 (N)
### tsconfig.json strictness
| Option | Value | Status |
### Type suppression audit
- @ts-ignore: N (N without comment) | as any in non-test: N | files with most suppressions
### Bundle analysis (if bundler configured)
- Total (gzip): X KB | Largest chunks | Whole-library imports | Tree-shaking enabled/disabled
### Source maps
- Format: external/inline/none | CI upload to error tracker: yes/no
### Path alias consistency
- tsconfig paths | Bundler match: yes/no | Jest moduleNameMapper match: yes/no
### Recommended fixes (priority order)
1. ...
Safety
- Run only
tsc --noEmit,grep, and read-only file inspection. Do not runtscwith emit,npm run build, or bundler commands unless the user asks (the reproducibility check is opt-in). - Do not modify
tsconfig.json,package.json, or bundler config without explicit user instruction. - Do not weaken strictness flags to make errors disappear; report them instead.
Completion criteria
Done means tsc --noEmit was run and its errors counted by file and code, strictness flags were audited, suppressions and any holes were listed with file:line, bundle and source-map config were assessed, alias consistency was checked, and fixes are ordered by priority.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.