typescript strict mode

TypeScript Strict Mode

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "typescript strict mode" with this command: npx skills add laurigates/claude-plugins/laurigates-claude-plugins-typescript-strict-mode

TypeScript Strict Mode

Modern TypeScript configuration with strict type checking for maximum safety and developer experience. This guide focuses on TypeScript 5.x best practices for 2025.

Core Expertise

What is Strict Mode?

  • Type safety: Catch more bugs at compile time

  • Better IDE experience: Improved autocomplete and refactoring

  • Maintainability: Self-documenting code with explicit types

  • Modern defaults: Align with current TypeScript best practices

Key Capabilities

  • Strict null checking

  • Strict function types

  • No implicit any

  • No unchecked indexed access

  • Proper module resolution

  • Modern import/export syntax enforcement

Recommended tsconfig.json (2025)

Minimal Production Setup

{ "compilerOptions": { // Type Checking "strict": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, "noUnusedParameters": true,

// Modules
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": false,
"verbatimModuleSyntax": true,

// Emit
"target": "ES2022",
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"outDir": "dist",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"removeComments": false,
"noEmit": false,

// Interop
"isolatedModules": true,
"allowJs": false,
"checkJs": false,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true

}, "include": ["src/**/*"], "exclude": ["node_modules", "dist", "build"] }

Vite/Bun Project (Bundler)

{ "compilerOptions": { // Type Checking - Maximum strictness "strict": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, "noUnusedParameters": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "exactOptionalPropertyTypes": true,

// Modules (Bundler for Vite/Bun)
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": false,
"verbatimModuleSyntax": true,

// Emit (Vite handles bundling)
"target": "ES2022",
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"jsx": "preserve", // Vite handles JSX
"noEmit": true, // Vite handles emit

// Interop
"isolatedModules": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,

// Path Mapping
"baseUrl": ".",
"paths": {
  "@/*": ["src/*"],
  "@components/*": ["src/components/*"]
}

}, "include": ["src/**/*", "vite.config.ts"], "exclude": ["node_modules", "dist"] }

Node.js Library (NodeNext)

{ "compilerOptions": { // Type Checking "strict": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true,

// Modules (NodeNext for Node.js)
"module": "NodeNext",
"moduleResolution": "NodeNext",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": false,
"esModuleInterop": true,
"verbatimModuleSyntax": true,

// Emit (Node.js library)
"target": "ES2022",
"lib": ["ES2023"],
"outDir": "dist",
"declaration": true,
"declarationMap": true,
"sourceMap": true,

// Interop
"isolatedModules": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true

}, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] }

Strict Flags Explained

strict: true (Umbrella Flag)

Enables all strict type-checking options:

{ "strict": true // Equivalent to: // "noImplicitAny": true, // "strictNullChecks": true, // "strictFunctionTypes": true, // "strictBindCallApply": true, // "strictPropertyInitialization": true, // "noImplicitThis": true, // "alwaysStrict": true }

Always enable strict: true for new projects.

noImplicitAny

Disallows variables with implicit any type.

// ❌ Error with noImplicitAny function add(a, b) { // ^ Error: Parameter 'a' implicitly has an 'any' type return a + b; }

// ✅ Correct function add(a: number, b: number): number { return a + b; }

strictNullChecks

null and undefined are distinct from other types.

// ❌ Error with strictNullChecks const name: string = null; // ^^^^ Error: Type 'null' is not assignable to type 'string'

// ✅ Correct const name: string | null = null;

// ✅ Correct (handle null explicitly) function greet(name: string | null): string { if (name === null) { return 'Hello, stranger!'; } return Hello, ${name}!; }

strictFunctionTypes

Function parameter types are checked contravariantly.

type Logger = (msg: string | number) => void;

// ❌ Error with strictFunctionTypes const log: Logger = (msg: string) => console.log(msg); // ^^^^^^^^^^^^^ Error: Type '(msg: string) => void' is not assignable

// ✅ Correct const log: Logger = (msg: string | number) => console.log(msg);

strictBindCallApply

Check that bind , call , and apply are invoked correctly.

function greet(name: string, age: number) { console.log(${name} is ${age} years old); }

// ❌ Error with strictBindCallApply greet.call(undefined, 'John', '25'); // ^^^^ Error: Argument of type 'string' is not assignable to 'number'

// ✅ Correct greet.call(undefined, 'John', 25);

strictPropertyInitialization

Class properties must be initialized.

class User { // ❌ Error with strictPropertyInitialization name: string; // ^^^^^^ Error: Property 'name' has no initializer

// ✅ Correct (initialize in constructor) name: string; constructor(name: string) { this.name = name; }

// ✅ Correct (default value) age: number = 0;

// ✅ Correct (definitely assigned assertion) id!: number; }

noImplicitThis

Disallow this with implicit any type.

// ❌ Error with noImplicitThis function logName() { console.log(this.name); // ^^^^ Error: 'this' implicitly has type 'any' }

// ✅ Correct (explicit this parameter) function logName(this: { name: string }) { console.log(this.name); }

Additional Strict Flags (Recommended for 2025)

noUncheckedIndexedAccess (Essential)

Index signatures return T | undefined instead of T .

// Without noUncheckedIndexedAccess const users: Record<string, User> = {}; const user = users['john']; // Type: User (wrong - might be undefined)

// ✅ With noUncheckedIndexedAccess const users: Record<string, User> = {}; const user = users['john']; // Type: User | undefined (correct)

if (user) { console.log(user.name); // Type narrowed to User }

Always enable this flag to prevent runtime errors.

noImplicitOverride

Require override keyword for overridden methods.

class Base { greet() { console.log('Hello'); } }

class Derived extends Base { // ❌ Error with noImplicitOverride greet() { // ^^^^^ Error: This member must have an 'override' modifier console.log('Hi'); }

// ✅ Correct override greet() { console.log('Hi'); } }

noPropertyAccessFromIndexSignature

Force bracket notation for index signatures.

type User = { name: string; [key: string]: string; };

const user: User = { name: 'John', email: 'john@example.com' };

// ❌ Error with noPropertyAccessFromIndexSignature console.log(user.email); // ^^^^^ Error: Property 'email' comes from index signature, use bracket notation

// ✅ Correct console.log(user['email']);

// ✅ Also correct (explicit property) console.log(user.name);

noFallthroughCasesInSwitch

Prevent fallthrough in switch statements.

function getDiscount(role: string): number { switch (role) { case 'admin': return 0.5; // ❌ Error with noFallthroughCasesInSwitch case 'user': // ^^^^ Error: Fallthrough case in switch console.log('User discount'); case 'guest': return 0.1; } }

// ✅ Correct function getDiscount(role: string): number { switch (role) { case 'admin': return 0.5; case 'user': console.log('User discount'); return 0.2; // Explicit return case 'guest': return 0.1; default: return 0; } }

exactOptionalPropertyTypes

Optional properties cannot be set to undefined explicitly.

type User = { name: string; age?: number; // Type: number | undefined (implicit) };

// ❌ Error with exactOptionalPropertyTypes const user: User = { name: 'John', age: undefined }; // ^^^^^^^^^ Error: Type 'undefined' is not assignable

// ✅ Correct (omit property) const user: User = { name: 'John' };

// ✅ Correct (assign a value) const user2: User = { name: 'Jane', age: 25 };

Module Resolution

moduleResolution: "Bundler" (Vite/Bun)

Use for projects with bundlers (Vite, Webpack, Bun).

{ "compilerOptions": { "module": "ESNext", "moduleResolution": "Bundler" } }

Features:

  • ✅ No file extensions required in imports

  • ✅ JSON imports without assertions

  • ✅ Package.json exports field support

  • ✅ Optimized for bundlers

// ✅ Works with Bundler import config from './config.json'; import { add } from './utils'; // No .ts extension

moduleResolution: "NodeNext" (Node.js)

Use for Node.js libraries and servers.

{ "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext" } }

Features:

  • ✅ Respects package.json type: "module"

  • ✅ Requires explicit .js extensions (even for .ts files)

  • ✅ Supports conditional exports

  • ✅ Aligned with Node.js ESM behavior

// ✅ Works with NodeNext (note .js extension) import { add } from './utils.js'; import config from './config.json' assert { type: 'json' };

package.json:

{ "type": "module", "exports": { ".": { "import": "./dist/index.js", "types": "./dist/index.d.ts" } } }

verbatimModuleSyntax (Recommended for 2025)

Prevents TypeScript from rewriting imports/exports.

{ "compilerOptions": { "verbatimModuleSyntax": true } }

Benefits:

  • ✅ Explicit type imports required

  • ✅ Prevents unintended side effects

  • ✅ Better tree-shaking

  • ✅ Aligns with future JavaScript standards

// ❌ Error with verbatimModuleSyntax import { User } from './types'; // ^^^^^^ Error: 'User' is a type and must be imported with 'import type'

// ✅ Correct import type { User } from './types';

// ✅ Correct (value import) import { fetchUser } from './api';

// ✅ Correct (mixed import) import { fetchUser, type User } from './api';

Replaces:

  • importsNotUsedAsValues (deprecated)

  • preserveValueImports (deprecated)

Path Mapping

{ "compilerOptions": { "baseUrl": ".", "paths": { "@/": ["src/"], "@components/": ["src/components/"], "@utils/": ["src/utils/"] } } }

Usage:

// Path aliases keep imports clean import { Button } from '@components/Button';

Vite/Bun configuration:

// vite.config.ts import { defineConfig } from 'vite'; import path from 'path';

export default defineConfig({ resolve: { alias: { '@': path.resolve(__dirname, './src'), '@components': path.resolve(__dirname, './src/components'), }, }, });

Migrating to Strict Mode

Step 1: Enable Gradually

{ "compilerOptions": { // Start with these "noImplicitAny": true, "strictNullChecks": false, // Enable later "strictFunctionTypes": true, "strictBindCallApply": true, "strictPropertyInitialization": false, // Enable later "noImplicitThis": true, "alwaysStrict": true } }

Step 2: Fix Errors Incrementally

Check errors without emitting

bunx tsc --noEmit

Fix files one at a time

bunx tsc --noEmit src/utils.ts

Step 3: Enable Remaining Flags

{ "compilerOptions": { "strict": true, // Enable all at once "noUncheckedIndexedAccess": true } }

Step 4: Optional Strict Flags

{ "compilerOptions": { "strict": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "exactOptionalPropertyTypes": true } }

Common Patterns

Handling Null/Undefined

// Non-null assertion (use sparingly) const element = document.getElementById('app')!;

// Optional chaining const name = user?.profile?.name;

// Nullish coalescing const displayName = user?.name ?? 'Anonymous';

// Type guard function isUser(value: unknown): value is User { return typeof value === 'object' && value !== null && 'name' in value; }

Index Signature Safety

// ❌ Unsafe const value = obj[key]; // Type: T (wrong)

// ✅ Safe with noUncheckedIndexedAccess const value = obj[key]; // Type: T | undefined

if (value !== undefined) { // Type: T console.log(value); }

// ✅ Safe with assertion const value = obj[key]; if (value === undefined) throw new Error('Key not found'); // Type: T (narrowed)

Troubleshooting

Too Many Errors

Enable flags gradually

Start with noImplicitAny, then add others

Use @ts-expect-error for temporary fixes

// @ts-expect-error - TODO: Fix this type const value: string = null;

Library Types Missing

Install type definitions

bun add --dev @types/node @types/react

Skip type checking for libraries

{ "compilerOptions": { "skipLibCheck": true } }

Module Resolution Errors

// Bundler: No extension needed import { add } from './utils';

// NodeNext: Requires .js extension import { add } from './utils.js';

// Check moduleResolution setting bunx tsc --showConfig | grep moduleResolution

References

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

python-code-quality

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

python-development

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

clippy-advanced

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

python-testing

No summary provided by upstream source.

Repository SourceNeeds Review