tsc

TSC (TypeScript Compiler) Tool Analysis

Overview

TSC is the TypeScript compiler that performs static type checking on TypeScript files. This analysis compares Lintro’s wrapper with core tsc behavior.

Core Tool Capabilities

  • Static type checking with full TypeScript type system
  • Config discovery: tsconfig.json with extends chain support
  • Build modes: --build for composite projects, --watch for development
  • Output: JavaScript emission, declaration files, sourcemaps
  • Flags: --strict, --noEmit, --skipLibCheck, --project, --target, --module, --moduleResolution, --paths, --baseUrl, and 100+ compiler options
  • Incremental compilation with --incremental and --tsBuildInfoFile
  • Project references for monorepo support

Lintro Implementation Analysis

✅ Preserved Features

  • ✅ Invokes tsc with --noEmit --pretty false for type checking without output
  • ✅ Respects native tsconfig.json compiler options (auto-discovered or via --project)
  • File targeting works even with tsconfig.json (see below)
  • ✅ Respects tsconfig.json include/exclude/files scoping (#851)
  • ✅ TypeScript project references (references) support (#803)
  • ✅ Multi-project monorepo discovery and per-project type checking (#805)
  • ✅ Per-project framework detection in monorepos
  • ✅ Supports --strict mode toggle
  • ✅ Supports --skipLibCheck for faster checks (enabled by default)
  • ✅ File discovery for *.ts, *.tsx, *.mts, *.cts
  • ✅ Intelligent command fallback: direct tsc -> bunx tsc -> npx tsc
  • ✅ Parses tsc output into structured ToolResult with file/line/column/code

File Targeting Behavior

The Problem: Native tsc ignores CLI file arguments when tsconfig.json exists, instead checking all files defined in the config’s include/files patterns.

Lintro’s Solution: By default, lintro respects your file selection even when tsconfig.json exists. This is achieved by creating a temporary tsconfig that:

  1. Extends your project’s tsconfig.json (preserving all compiler options)
  2. Overrides include to target only the files you specified
# Check only specific files (default behavior - lintro respects file targeting)
lintro check src/utils.ts src/helpers.ts --tools tsc
# → Creates temp config extending tsconfig.json but only checking these 2 files

# Check all files defined in tsconfig.json (native behavior)
lintro check . --tools tsc --tool-options "tsc:use_project_files=True"
# → Uses tsconfig.json as-is, checks all files in include/files patterns

This gives you the best of both worlds:

  • Default: Lintro-style file targeting with tsconfig.json compiler options
  • Opt-in: Native tsconfig.json file selection when needed

Respecting tsconfig scoping (#851): When your tsconfig.json has an explicit include, files, or exclude field, lintro now respects it — running tsc --project <tsconfig> directly instead of generating a temp config that overrides the scoping. This prevents false positives from files the project intentionally excludes (e.g., vitest.config.ts).

Monorepo Support (#803, #805)

Lintro automatically discovers TypeScript sub-projects in monorepos:

  1. Project references: Follows references arrays in tsconfig.json recursively
  2. Tree walking: Discovers tsconfig.json files in subdirectories
  3. Deepest wins: When parent and child tsconfigs overlap, files are assigned to the deepest (most specific) tsconfig, preventing double-checking under conflicting compiler options
  4. Per-project framework detection: Astro/Vue/Svelte detection is scoped per sub-project, not globally
# Monorepo with project references — each sub-project checked independently
lintro check . --tools tsc
# → packages/api: tsc -p packages/api/tsconfig.json
# → packages/web: skipped (Vue detected, use vue-tsc)
# → packages/lib: tsc -p packages/lib/tsconfig.json

⚠️ Limited / Missing

Build & Watch Modes:

  • ❌ No --watch mode (continuous compilation)
  • ❌ No --build mode (composite project building)
  • ❌ No --incremental caching (each run is fresh)

Output Generation:

  • ❌ No JavaScript emission (always uses --noEmit)
  • ❌ No declaration file generation (--declaration, --declarationMap)
  • ❌ No sourcemap generation (--sourceMap, --inlineSourceMap)
  • ❌ No output directory control (--outDir, --outFile)

Compiler Options (config-file-only):

  • ⚠️ target, module, moduleResolution - must be set in tsconfig.json
  • ⚠️ paths, baseUrl, rootDir, rootDirs - must be set in tsconfig.json
  • ⚠️ lib, types, typeRoots - must be set in tsconfig.json
  • ⚠️ esModuleInterop, allowSyntheticDefaultImports - must be set in tsconfig.json
  • ⚠️ jsx, jsxFactory, jsxFragmentFactory - must be set in tsconfig.json
  • ⚠️ experimentalDecorators, emitDecoratorMetadata - must be set in tsconfig.json
  • ⚠️ All other compilerOptions not exposed via --tool-options

Advanced Features:

  • ❌ No plugins configuration
  • ❌ No --generateTrace performance profiling
  • ❌ No custom diagnostic formatting
  • ❌ No --listFiles, --listEmittedFiles introspection

🚀 Enhancements

  • ✅ Safe timeout handling (default 60s) with structured timeout result
  • ✅ Auto config discovery prioritizes tsconfig.json in working directory
  • Smart file targeting via temp tsconfig (preserves compiler options)
  • ✅ Normalized ToolResult with parsed issues from tsc_parser
  • ✅ Priority 82, tool type LINTER | TYPE_CHECKER, same as mypy
  • ✅ Windows path normalization in parser output
  • ✅ Graceful handling when tsc is not installed with helpful install hints
  • Dependency error categorization - separates missing module errors from type errors
  • Auto-install support - optionally install Node.js deps before running tsc

Dependency Error Handling

Lintro intelligently categorizes tsc errors to distinguish between actual type errors and errors caused by missing dependencies (e.g., when node_modules is not installed).

Error Categories

Dependency errors (TS2307, TS2688, TS7016):

  • TS2307: Cannot find module ‘X’ or its corresponding type declarations
  • TS2688: Cannot find type definition file for ‘X’
  • TS7016: Could not find a declaration file for module ‘X’

Type errors (all other codes):

  • Actual TypeScript type checking issues in your code

Output Example

When dependency errors are detected, Lintro shows them separately with actionable guidance:

Results: tsc
├── Type errors: 3
│   └── src/utils.ts:15 - Type 'string' not assignable to 'number'
│   └── ...
└── Missing dependencies: 4
    └── vite/client, react, @types/node, ...

    Suggestions:
    - Run 'bun install' or 'npm install'
    - Use '--auto-install' flag

Auto-Install Dependencies

Use the --auto-install flag to automatically install Node.js dependencies before running tsc:

# Auto-install deps then run tsc
lintro check src/ --tools tsc --auto-install

# Enable globally in config
# .lintro-config.yaml
execution:
  auto_install_deps: true

Package manager preference:

  1. bun install --frozen-lockfile (falls back to bun install)
  2. npm ci (falls back to npm install)

Note: In Docker, dependencies are auto-installed by default when the container starts.

Usage Comparison

# Core tsc - type check only (checks all files in tsconfig.json)
tsc --noEmit

# Core tsc - with specific config
tsc --project tsconfig.app.json --noEmit

# Lintro wrapper - check specific files (respects file targeting)
lintro check src/utils.ts --tools tsc

# Lintro wrapper - check directory (finds all .ts/.tsx files)
lintro check src/ --tools tsc

# Lintro wrapper - use tsconfig.json file selection (native behavior)
lintro check . --tools tsc --tool-options "tsc:use_project_files=True"

# Lintro wrapper - enable strict mode override
lintro check src/ --tools tsc --tool-options "tsc:strict=True"

# Lintro wrapper - use specific config file
lintro check src/ --tools tsc --tool-options "tsc:project=tsconfig.build.json"

Configuration Strategy

  • File targeting preserved: Lintro respects your file selection by default
  • Compiler options inherited: All settings from tsconfig.json are preserved
  • No config injection: Lintro cannot modify tsconfig.json settings; tool is “Native only”
  • Tool options available:
    • tsc:project (string) - path to tsconfig.json file
    • tsc:strict (bool) - enable --strict flag
    • tsc:skip_lib_check (bool) - enable --skipLibCheck (default: true)
    • tsc:use_project_files (bool) - use tsconfig.json’s include/files patterns instead of lintro’s file targeting (default: false)
    • tsc:timeout (int) - execution timeout in seconds (default: 60)
  • Config display: lintro config -v shows parsed tsconfig.json compilerOptions

Priority and Conflicts

  • Priority: 82 (runs after formatters/linters, before tests)
  • Tool Type: LINTER | TYPE_CHECKER
  • Conflicts: None
  • Complements: oxlint, oxfmt, prettier (formatting/linting)

Recommendations

  • Use Lintro when you want quick type checking integrated into a multi-tool workflow with normalized output and timeout safety.
  • Use core tsc directly when you need:
    • Watch mode for development (tsc --watch)
    • Build mode for composite projects (tsc --build)
    • Incremental compilation for large projects
    • JavaScript/declaration file output
    • Fine-grained compiler option control beyond tsconfig.json