TypeScript 3 ๐ท The TypeScript CLI and tsconfig.json
The TypeScript CLI (tsc) is the program that reads your .ts files, type-checks them, and emits .js. But tsc on its own does almost nothing useful โ it needs configuration. That configuration lives in tsconfig.json, a single file at the project root that tells the compiler which files to include, what JavaScript version to target, how strict to be, and where to write the output. Learning tsc and tsconfig.json together is unavoidable โ every TypeScript project has both, and understanding them is what turns “I write .ts and hope for the best” into “I control exactly what the compiler does.”
Key point: tsconfig.json is not optional. Without it, tsc treats every file you pass as an isolated compile and applies defaults you didn’t choose. With it, the compiler knows your project โ its files, its target, its strictness. Every real TypeScript project has a tsconfig.json, and every serious TypeScript developer reads it.
The CLI โ what tsc does
tsc is the TypeScript compiler. Invoked with no arguments in a directory that has a tsconfig.json, it reads the config and compiles the project. Invoked with file paths, it compiles those files with default settings.
tsc # Compile per tsconfig.json
tsc file.ts # Compile a single file
tsc --init # Generate a starter tsconfig.json
tsc --watch # Rebuild on change
tsc --noEmit # Type-check only, no output
tsc --project path/ # Use a specific tsconfig
tsc --build tsconfig.json # Build a project (references)
tsc --help # Show all flags
Two modes:
| Mode | Trigger | Behavior |
|---|---|---|
| Project mode | tsc (no args, tsconfig.json present) | Compile everything the config includes |
| File mode | tsc file.ts | Compile one file with CLI flags only |
Project mode is what you use 99% of the time. File mode is fine for one-off checks but doesn’t scale โ you’d have to list every file and every flag on the command line.
Exit codes matter in CI:
0โ no errors, compilation succeeded1โ errors found2โ configuration error (bad tsconfig, missing file)
That’s what makes tsc --noEmit a valid CI step โ a non-zero exit code fails the build.
Why the distinction between project and file mode: File mode ignores
tsconfig.jsonentirely. If you runtsc index.tsin a project that has strict mode enabled, you get the default (loose) settings instead. That’s a common source of confusion โ the editor says one thing,tsc index.tssays another. Usetscwithout file arguments when you want the project’s settings.
The tsconfig.json file
tsconfig.json is a JSON file (with comments allowed โ it’s parsed as JSONC) that configures the compiler. Its presence in a directory marks that directory as the project root. It has a few top-level keys:
| Key | Purpose |
|---|---|
compilerOptions | How the compiler behaves |
include | Glob patterns for files to include |
exclude | Globs to exclude |
files | Explicit list of files (rare) |
extends | Inherit from another config |
references | Project references (monorepos) |
Most projects use compilerOptions plus include (and sometimes exclude). The other keys are for specific scenarios.
A minimal, modern config:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}
What each line does:
targetโ the JavaScript version to emitmoduleโ the module system for the outputmoduleResolutionโ how imports are resolvedstrictโ turns on all strict checksesModuleInteropโ smooth interop between CommonJS and ESMskipLibCheckโ skip type-checking of.d.tsfiles (faster builds)forceConsistentCasingInFileNamesโ reject imports that differ only in case (important on macOS/Windows)outDir/rootDirโ where output goes, where source livesincludeโ files to compile
tsc --init generates a config with most of these commented out and explained. It’s worth reading through once.
Why JSON with comments: TypeScript’s team wanted
tsconfig.jsonto be self-documenting.tsc --initwrites a file where every option has a// explanationnext to it. That’s not standard JSON, buttscaccepts it (and most editors highlight it correctly).
compilerOptions โ the ones that matter
There are over 100 compiler options. Most projects use about fifteen. Here are the ones that actually change how you write code.
target โ output JavaScript version
| Target | Emits | Use when |
|---|---|---|
ES5 | Old syntax, polyfilled helpers | Legacy browsers |
ES2017 | Modern async/await | Node 8+ |
ES2020 | Optional chaining, nullish | Node 14+ |
ES2022 | Top-level await, class fields | Node 16+, modern browsers |
ESNext | Latest supported | New projects |
target decides which syntax the compiler downlevels. If you target ES5, async/await becomes a state machine. If you target ES2022, it stays as async/await.
module โ output module system
| Module | Use when |
|---|---|
CommonJS | Node with require() |
ESNext | Bundlers (Vite, webpack) |
NodeNext | Modern Node with ESM |
Node16 | Node 16+ with mixed CJS/ESM |
module decides what import/export becomes in the emitted .js. For Node projects, NodeNext is the modern choice. For bundlers, ESNext.
strict โ the master switch
"strict": true
Turning this on enables all of these:
strictNullChecksโnullandundefinedare distinct from other typesstrictFunctionTypesโ contravariant function parameter checksstrictBindCallApplyโbind/call/applyare typedstrictPropertyInitializationโ class properties must be initializednoImplicitAnyโ reject implicitanynoImplicitThisโ reject implicitthisuseUnknownInCatchVariablesโ catch variables areunknown, notany
Every new project should start with strict: true. It’s the single option that most improves code quality.
What it costs: some existing code won’t compile without changes. Implicit anys need annotations. Null checks need ?. or guards. That’s the point โ the compiler makes you handle cases you were silently ignoring.
esModuleInterop
"esModuleInterop": true
Without this, importing a CommonJS module (like express) requires import * as express from 'express'. With it, import express from 'express' works. Turn it on. The allowSyntheticDefaultImports companion is included automatically.
skipLibCheck
"skipLibCheck": true
Skips type-checking of declaration files in node_modules. Dramatically faster, and in practice, library .d.ts files are already correct enough. Turn it on.
forceConsistentCasingInFileNames
"forceConsistentCasingInFileNames": true
Rejects imports that differ from the file’s real name only in case. Matters on macOS and Windows where the filesystem is case-insensitive โ otherwise Linux CI will fail with “file not found” for code that “worked on your machine.”
outDir and rootDir
"outDir": "dist",
"rootDir": "src"
rootDir is the source root. outDir is where .js goes. When both are set, tsc mirrors the source tree inside outDir โ src/a/b.ts becomes dist/a/b.js.
Without them, tsc writes .js next to .ts โ messy for real projects.
Why so few options matter: The default
tsc --initgenerates a file with dozens of options, most commented out. The active ones do most of the work. Learn these, and you understand 95% of TypeScript configs you’ll encounter.
include, exclude, files
These control which files the compiler sees.
include โ glob patterns:
"include": ["src/**/*.ts", "src/**/*.tsx"]
** matches any number of directories; * matches any name within a level. Common patterns:
| Pattern | Matches |
|---|---|
src/**/*.ts | All .ts files under src/ |
src/**/* | Everything under src/ |
**/*.ts | All .ts everywhere except excluded |
src/*.ts | .ts directly in src/ (not subdirs) |
exclude โ patterns to remove:
"exclude": ["node_modules", "dist", "**/*.spec.ts"]
By default, node_modules, bower_components, jspm_packages, and outDir are excluded. Adding to exclude removes more.
files โ an explicit list:
"files": ["src/index.ts", "src/cli.ts"]
files is rarely used. It bypasses globbing and compiles exactly those files. Useful for small scripts or specific build targets.
Interaction: include and exclude work together. files overrides both โ if you list files explicitly, they’re included regardless of exclude. Files that include picks up but exclude removes are dropped.
A note on exclude and include paths: All paths in these fields are relative to the tsconfig.json file’s directory. Patterns without leading ./ are globs; with ./ they’re literal.
Why globs and not file lists: A project with hundreds of files would need constant
tsconfig.jsonupdates. Globs let the config describe “everything undersrc/” once. New files are picked up automatically.
Inheritance with extends
extends lets one config inherit from another. Values are merged, with the child overriding the parent.
// tsconfig.base.json
{
"compilerOptions": {
"strict": true,
"target": "ES2022",
"module": "ESNext",
"esModuleInterop": true,
"skipLibCheck": true
}
}
// tsconfig.json
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}
The child inherits strict, target, module, etc., and adds outDir/rootDir. This is how monorepos and framework scaffolds share config.
Common uses:
- Framework scaffolds โ
@tsconfig/node20,@tsconfig/vite-react,@tsconfig/strictest - Monorepos โ a base config for all packages, each with its own overrides
- Multi-target projects โ a server config and a client config extending a shared base
Rules to know:
extendsis resolved relative to the file doing the extendingcompilerOptionsmerges โ the child wins on conflictsfiles,include,excludeare not merged โ the child’s values replace the parent’s- Paths in the parent are resolved relative to the parent, not the child
Why inheritance was added: Framework authors wanted to ship “the recommended config for TypeScript on Node 20” as a package. Without
extends, every project copied the same 20 lines. Withextends, they reference a package and get updates.
Project references
For large codebases and monorepos, references lets one tsconfig.json depend on another. tsc --build then compiles them in dependency order and uses incremental builds.
// tsconfig.json (root)
{
"references": [
{ "path": "./packages/shared" },
{ "path": "./packages/api" }
],
"files": []
}
Each referenced project has its own tsconfig.json with composite: true.
// packages/shared/tsconfig.json
{
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}
What composite: true enables:
declarationis forced on โ.d.tsfiles are emittedincrementalis forced on โ.tsbuildinfocaches between builds- The project can be referenced by others
Building:
tsc --build # Build all projects in dependency order
tsc --build --watch # Watch and rebuild changed projects only
tsc --build --clean # Remove build output
Why use it: In a monorepo, rebuilding everything on every change is slow. Project references let tsc rebuild only what changed, in the right order. That’s the mechanism behind fast monorepo builds in Nx, Turborepo, and similar tools.
Why
compositeand references exist: TypeScript was designed for single projects. As monorepos grew, the compiler needed to understand a project’s dependencies and only rebuild what changed. Project references are that mechanism โ and the foundation of incremental builds.
A worked example
A small Node project compiled to dist/.
package.json:
{
"name": "ts-cli-demo",
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "tsc",
"watch": "tsc --watch",
"typecheck": "tsc --noEmit",
"start": "node dist/index.js"
},
"devDependencies": {
"typescript": "^5.4.0"
}
}
tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist", "**/*.spec.ts"]
}
src/index.ts:
interface User {
id: number;
name: string;
}
function greet(user: User): string {
return `Hello, ${user.name} (id ${user.id})!`;
}
const alice: User = { id: 1, name: 'Alice' };
console.log(greet(alice));
Build:
npm run build
Output:
dist/
โโโ index.d.ts
โโโ index.js
โโโ index.js.map
.d.ts is the type declaration for other TypeScript projects. .js is the executable. .js.map maps runtime errors back to source.
Run:
node dist/index.js
# Hello, Alice (id 1)!
Try an error: change id: 1 to id: '1'. npm run build fails with a precise error and no output is emitted. That’s the compiler doing its job.
Why
declaration: trueandsourceMap: true: Declaration files make your code importable from other TypeScript projects. Source maps make debugging runtime errors point back to the original.tsline. For libraries and servers, both are near-mandatory.
tsc --init โ the generated config
Running tsc --init creates a tsconfig.json with every option listed and commented out, and a reasonable subset enabled. It’s long. Here’s the essence:
{
"compilerOptions": {
"target": "ES2016",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
That’s the minimal generated config. Change target and module for your environment. Leave strict: true on.
If you’re on Node 20+ and using ESM, replace module with NodeNext and moduleResolution with NodeNext. If you’re using a bundler, use module: ESNext, moduleResolution: Bundler.
Why start with
--initand not a blank file: The generated file is a checklist. Reading through it once is one of the best ways to learn what options exist. You don’t need to enable them all โ you need to know they’re there.
Complete Example Session
# ============================================
# PART 1: CREATE A FRESH PROJECT
# ============================================
mkdir tsconfig-demo
cd tsconfig-demo
npm init -y
npm install --save-dev typescript
# ============================================
# PART 2: GENERATE A TSCONFIG
# ============================================
npx tsc --init
# Created a new tsconfig.json with:
# target: es2016
# module: commonjs
# strict: true
# ...
# ============================================
# PART 3: EDIT THE TSCONFIG
# ============================================
cat > tsconfig.json << 'EOF'
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist", "**/*.spec.ts"]
}
EOF
# ============================================
# PART 4: WRITE SOURCE
# ============================================
mkdir src
cat > src/index.ts << 'EOF'
interface User { id: number; name: string; }
export function greet(user: User): string {
return `Hello, ${user.name}!`;
}
const alice: User = { id: 1, name: 'Alice' };
console.log(greet(alice));
EOF
# ============================================
# PART 5: COMPILE
# ============================================
npx tsc
# (no output โ success)
ls dist/
# [ index.d.ts index.js index.js.map ]
# ============================================
# PART 6: RUN
# ============================================
node dist/index.js
# Hello, Alice!
# ============================================
# PART 7: TRIGGER AN ERROR
# ============================================
cat > src/broken.ts << 'EOF'
const x: number = 'not a number';
EOF
npx tsc
# [ src/broken.ts:1:7 - error TS2322: Type 'string' is not assignable to type 'number'. ]
# (no output emitted for broken.ts)
rm src/broken.ts
# ============================================
# PART 8: WATCH MODE
# ============================================
npx tsc --watch
# [ Watching for file changes. ]
# (edit src/index.ts in another terminal โ dist/ updates)
# ============================================
# PART 9: TYPE-CHECK ONLY
# ============================================
npx tsc --noEmit
# (no output โ no errors)
# exit code 0
# ============================================
# PART 10: INHERIT FROM A BASE
# ============================================
cat > tsconfig.base.json << 'EOF'
{
"compilerOptions": {
"target": "ES2022",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
EOF
cat > tsconfig.json << 'EOF'
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "dist",
"rootDir": "src",
"declaration": true
},
"include": ["src"]
}
EOF
npx tsc
# (still compiles cleanly with the inherited config)
Quick Reference
CLI Commands
| Command | Purpose |
|---|---|
tsc | Compile per tsconfig.json |
tsc file.ts | Compile a single file (ignores tsconfig) |
tsc --init | Generate starter config |
tsc --watch | Rebuild on change |
tsc --noEmit | Type-check only |
tsc --project path | Use a specific config |
tsc --build | Build a project (references) |
tsc --build --clean | Clean build output |
tsc --help | Full flag list |
tsc --version | Show compiler version |
Exit Codes
| Code | Meaning |
|---|---|
| 0 | No errors |
| 1 | Errors found |
| 2 | Config error |
Top-Level Keys
| Key | Purpose |
|---|---|
compilerOptions | Compiler behavior |
include | Globs for files to compile |
exclude | Globs to exclude |
files | Explicit file list (overrides include/exclude) |
extends | Inherit from another config |
references | Project references |
Essential compilerOptions
| Option | Recommended |
|---|---|
target | ES2022 or ESNext |
module | ESNext, NodeNext, or CommonJS |
moduleResolution | Bundler or NodeNext |
strict | true |
esModuleInterop | true |
skipLibCheck | true |
forceConsistentCasingInFileNames | true |
outDir | dist |
rootDir | src |
declaration | true for libraries |
sourceMap | true for debugging |
strict Enables
| Sub-option | Effect |
|---|---|
strictNullChecks | null / undefined are distinct |
strictFunctionTypes | Contravariant parameter checks |
strictBindCallApply | Typed bind/call/apply |
strictPropertyInitialization | Class props must be set |
noImplicitAny | Reject implicit any |
noImplicitThis | Reject implicit this |
useUnknownInCatchVariables | catch is unknown |
Common Glob Patterns
| Pattern | Matches |
|---|---|
src/**/*.ts | All .ts under src/ |
src/**/* | Everything under src/ |
**/*.ts | All .ts anywhere (minus exclude) |
src/*.ts | .ts in src/ only |
!src/**/*.spec.ts | Negation in files arrays |
extends Behavior
| Key | Merged? |
|---|---|
compilerOptions | โ child wins |
include | โ child replaces |
exclude | โ child replaces |
files | โ child replaces |
references | โ child replaces |
Project References
| Option | Purpose |
|---|---|
composite: true | Enable referencing |
declaration: true | Emit .d.ts (auto with composite) |
incremental: true | Cache builds (auto with composite) |
tsc --build | Build in dependency order |
tsc --build --watch | Incremental watch |
Best Practices
โ Do This:
// Enable strict mode
"strict": true // โ
// Modern target
"target": "ES2022" // โ
// Separate source and output
"rootDir": "src", "outDir": "dist" // โ
// Skip lib check for speed
"skipLibCheck": true // โ
// Ensure consistent case
"forceConsistentCasingInFileNames": true // โ
// Emit declarations for libraries
"declaration": true // โ
// Use project references for monorepos
"references": [{ "path": "./packages/shared" }] // โ
// Use extends to share config
"extends": "@tsconfig/node20/tsconfig.json" // โ
// Type-check in CI
npx tsc --noEmit // โ
โ Don’t Do This:
// Don't turn off strict permanently
"strict": false // โ
// Don't mix source and output
// No rootDir / outDir // โ ๏ธ .js next to .ts
// Don't edit tsconfig without understanding
// Random changes break builds // โ ๏ธ
// Don't include node_modules explicitly
"include": ["node_modules"] // โ
// Don't mix file mode and project mode
tsc file.ts // โ ๏ธ ignores tsconfig
// Don't commit tsbuildinfo in some setups
git add tsconfig.tsbuildinfo // โ ๏ธ depends on workflow
// Don't use target ES5 for modern Node
"target": "ES5" // โ downlevels everything
// Don't duplicate config across monorepo packages
// Copy-pasting a full tsconfig // โ ๏ธ use extends
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
Running tsc file.ts in a project | Ignores tsconfig.json | Run tsc (no args) |
Forgetting rootDir | .js next to .ts | Set rootDir and outDir |
Wrong moduleResolution | Imports don’t resolve | Match to environment |
strict: false long-term | Weak checking | Enable strict |
include misses files | Not compiled | Check globs |
extends paths wrong | Config not found | Paths resolve from extending file |
| Ignoring exit codes in CI | False passing builds | Check exit codes |
Editing node_modules | Overwritten on install | Never edit โ install |
Missing skipLibCheck | Slow builds | Enable it |
| Incompatible target/module | Runtime errors | Match to runtime |
Real-World Examples
1. Minimal config
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"strict": true
}
}
2. Library config
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"declaration": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}
3. Node app config
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*.ts"]
}
4. Bundler app config (Vite, esbuild)
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"jsx": "react-jsx",
"noEmit": true
},
"include": ["src"]
}
5. Config with extends
{
"extends": "@tsconfig/node20/tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}
6. Exclude tests
{
"exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"]
}
7. Explicit files
{
"files": ["src/cli.ts", "src/index.ts"]
}
8. Watch and rebuild
npx tsc --watch
9. Type-check in CI
npx tsc --noEmit
10. Emit declarations
"declaration": true
11. Enable source maps
"sourceMap": true
12. Isolated modules
"isolatedModules": true
For bundler-based workflows โ ensures each file can be compiled independently.
13. Project references
{
"references": [
{ "path": "./packages/shared" },
{ "path": "./packages/api" }
],
"files": []
}
14. Composite project
{
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}
15. Build all projects
npx tsc --build
16. Clean builds
npx tsc --build --clean
17. Check a config without compiling
npx tsc --showConfig
Prints the effective config after merging extends.
18. Find which tsconfig is used
npx tsc --showConfig | head -5
19. Add a build script
"scripts": {
"build": "tsc",
"watch": "tsc --watch",
"typecheck": "tsc --noEmit"
}
20. Print help for a flag
npx tsc --help | grep watch
Visual: Config Resolution
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ tsc (no args) โ
โ โ โ
โ โผ โ
โ Find tsconfig.json โ
โ (walk up from cwd) โ
โ โ โ
โ โผ โ
โ Read extends chain โ
โ โ โ
โ โผ โ
โ Merge compilerOptions โ
โ (child overrides parent) โ
โ โ โ
โ โผ โ
โ Apply include/exclude โ
โ โ โ
โ โผ โ
โ Compile files โ
โ โ โ
โ โผ โ
โ Emit to outDir โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: extends Merge
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ tsconfig.base.json โ
โ โ
โ target: ES2022 โ
โ strict: true โ
โ esModuleInterop: true โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ extends
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ tsconfig.json โ
โ โ
โ extends: ./tsconfig.base.json โ
โ outDir: dist โ
โ rootDir: src โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ merged
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Effective config โ
โ โ
โ target: ES2022 (inherited) โ
โ strict: true (inherited) โ
โ esModuleInterop: true (inherited) โ
โ outDir: dist (own) โ
โ rootDir: src (own) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Project References
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ tsconfig.json (root) โ
โ โ
โ references: โ
โ โ packages/shared โ
โ โ packages/api โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โผ โผ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
โ shared/ โ โ api/ โ
โ โ โ โ
โ composite: true โโโโโโ references: shared โ
โ declaration โ โ โ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ tsc --build โ
โ โ
โ Builds shared first, then api โ
โ Rebuilds only what changed โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Project Layout with tsconfig
my-project/
โโโ src/
โ โโโ index.ts โ source
โ โโโ utils.ts
โโโ dist/
โ โโโ index.js โ emitted
โ โโโ index.d.ts โ emitted
โ โโโ index.js.map โ emitted
โ โโโ utils.js
โโโ node_modules/
โโโ package.json
โโโ package-lock.json
โโโ tsconfig.json โ compiler config
โโโ tsconfig.base.json โ shared (if any)
โโโ .gitignore
Visual: Watch Mode Loop
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ tsc --watch โ
โ โ โ
โ โโโโบ initial full build โ
โ โ โ
โ โโโโบ watch for file changes โ
โ โ
โ Edit src/index.ts โ
โ โ โ
โ โผ โ
โ Incremental rebuild โ
โ โ โ
โ โผ โ
โ dist/index.js updated โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Strict Mode Effects
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ strict: false โ
โ โ
โ function f(x) { } โ implicit any โ
โ let name: string = null; โ allowed โ
โ obj.method(); โ this unchecked โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ strict: true โ
โ โ
โ function f(x) { } โ error: type x โ
โ let name: string = null; โ error: null โ
โ obj.method(); โ this checked โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Concept | Meaning |
|---|---|
tsc | The TypeScript compiler |
| Project mode | Compiles per tsconfig.json |
| File mode | Compiles one file, ignores config |
tsconfig.json | Compiler configuration |
compilerOptions | How the compiler behaves |
include / exclude | File selection globs |
files | Explicit file list |
extends | Inherit from another config |
references | Project references for monorepos |
composite | Enables referencing |
strict | Enables all strict sub-flags |
target | Output JS version |
module | Output module system |
outDir / rootDir | Output and source roots |
Key takeaways:
tscis the compiler; run it with no args to use the project’stsconfig.jsontsc file.tsignorestsconfig.jsonโ avoid in real projectstsconfig.jsonconfigures the project โstrict: true, moderntarget, and separatedoutDir/rootDirare the baselinecompilerOptionshas hundreds of flags โ about fifteen matter for most projectsinclude/excludecontrol which files are compiled โ globs, not listsextendsshares config between projects โ the child overrides the parentreferences+compositeenable incremental monorepo buildstsc --buildcompiles projects in dependency ordertsc --noEmittype-checks without emitting โ perfect for CItsc --watchrecompiles on change โ for the dev loop- Exit codes matter โ 0 means success, non-zero means errors
- Use
tsc --showConfigto see the effective merged config
Remember: tsconfig.json is where you decide what TypeScript is for this project โ how strict, what target, what files, what output. tsc reads it, applies it, and either produces JavaScript or produces errors. Learn the fifteen options that matter, use extends to share them, and add references when the project grows into a monorepo. Everything else is a variation on this foundation โ and you’ll see the same patterns in every TypeScript project you work with.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!