typescript-strict
TypeScript strict mode patterns. Use when writing any TypeScript code.
Install
mkdir -p .claude/skills/typescript-strict && curl -L -o skill.zip "https://mcp.directory/api/skills/download/1812" && unzip -o skill.zip -d .claude/skills/typescript-strict && rm skill.zipInstalls to .claude/skills/typescript-strict
About this skill
TypeScript Strict Mode
Core Rules
- No
any- ever. Useunknownif type is truly unknown - No type assertions (
as Type) without justification - Prefer
typeoverinterfacefor data structures - Reserve
interfacefor behavior contracts only
Schema Organization
Organize Schemas by Usage
Common patterns:
- Centralized:
src/schemas/for shared schemas - Co-located: Near the modules that use them
- Layered: Separate by architectural layer (if using layered/hexagonal architecture)
Key principle: Avoid duplicating the same validation logic across multiple files.
Gotcha: Schema Duplication
Common anti-pattern:
Defining the same schema in multiple places:
- Validation logic duplicated across endpoints
- Same business rules defined in multiple adapters
- Type definitions not shared
Why This Is Wrong:
- ❌ Duplication creates multiple sources of truth
- ❌ Changes require updating multiple files
- ❌ Breaks DRY principle at the knowledge level
- ❌ Domain logic leaks into infrastructure code
Solution:
// ✅ CORRECT - Define schema once, import everywhere
// src/schemas/user-requests.ts
import { z } from 'zod';
export const CreateUserRequestSchema = z.object({
email: z.string().email(),
name: z.string().min(1),
});
export type CreateUserRequest = z.infer<typeof CreateUserRequestSchema>;
// Use in multiple places
import { CreateUserRequestSchema } from '../schemas/user-requests.js';
// Express endpoint
app.post('/users', (req, res) => {
const result = CreateUserRequestSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ error: result.error });
}
// Use result.data (validated)
});
// GraphQL resolver
const createUser = (input: unknown) => {
const validated = CreateUserRequestSchema.parse(input);
return userService.create(validated);
};
Key Benefits:
- ✅ Single source of truth for validation
- ✅ Schema changes propagate everywhere automatically
- ✅ Type safety maintained across codebase
- ✅ DRY principle at knowledge level
Remember: If validation logic is duplicated, extract it into a shared schema.
Dependency Injection Pattern
Inject Dependencies, Don't Create Them
The Rule:
- Dependencies are always injected via parameters
- Never use
newto create dependencies inside functions - Factory functions accept dependencies as parameters
Why This Matters
Without dependency injection:
- ❌ Only one implementation possible
- ❌ Can't test with mocks (poor testability)
- ❌ Tight coupling to specific implementations
- ❌ Violates dependency inversion principle
- ❌ Can't swap implementations
With dependency injection:
- ✅ Any implementation works (in-memory, database, remote API)
- ✅ Fully testable (inject mocks for testing)
- ✅ Loose coupling
- ✅ Follows dependency inversion principle
- ✅ Runtime flexibility (configure implementation)
Example: Order Processor
❌ WRONG - Creating implementation internally
export const createOrderProcessor = ({
paymentGateway,
}: {
paymentGateway: PaymentGateway;
}): OrderProcessor => {
// ❌ Hardcoded implementation!
const orderRepository = new InMemoryOrderRepository();
return {
processOrder(order) {
const payment = paymentGateway.charge(order.total);
if (!payment.success) {
return { success: false, error: payment.error };
}
orderRepository.save(order); // Using hardcoded repository
return { success: true, data: order };
},
};
};
Why this is WRONG:
- Only ONE repository implementation possible (in-memory)
- Can't test with mock repository
- Can't swap to database repository or remote API
- Tight coupling to specific implementation
✅ CORRECT - Injecting all dependencies
export const createOrderProcessor = ({
paymentGateway, // ✅ Injected
orderRepository, // ✅ Injected
}: {
paymentGateway: PaymentGateway;
orderRepository: OrderRepository;
}): OrderProcessor => {
return {
processOrder(order) {
const payment = paymentGateway.charge(order.total);
if (!payment.success) {
return { success: false, error: payment.error };
}
orderRepository.save(order); // Delegate to injected dependency
return { success: true, data: order };
},
};
};
Why this is CORRECT:
- ✅ Any OrderRepository implementation works (in-memory, PostgreSQL, MongoDB)
- ✅ Any PaymentGateway implementation works (Stripe, mock, testing)
- ✅ Easy to test (inject mocks)
- ✅ Loose coupling (depends on interfaces, not implementations)
- ✅ Runtime flexibility (choose implementation at startup)
Type vs Interface - Understanding WHY
The choice between type and interface is architectural, not stylistic.
Behavior Contracts → Use interface
When to use: Interfaces define contracts that must be implemented.
Examples: UserRepository, PaymentGateway, EmailService, CacheProvider
Why interface for behavior contracts?
-
Signals implementation contracts clearly
- Interface communicates "this must be implemented elsewhere"
- Type communicates "this is a data structure"
-
Better TypeScript errors when implementing
class X implements UserRepositorygives clear errors- Types don't have
implementskeyword
-
Conventional for dependency injection
- Standard pattern for dependency inversion
- Clear separation between contract and implementation
-
Class-friendly for implementations
- Many libraries use classes for services
- Classes naturally implement interfaces
Example:
// Behavior contract
export interface UserRepository {
findById(id: string): Promise<User | undefined>;
save(user: User): Promise<void>;
delete(id: string): Promise<void>;
}
// Concrete implementation
export class PostgresUserRepository implements UserRepository {
async findById(id: string): Promise<User | undefined> {
// Implementation
}
// ... other methods
}
Data Structures → Use type
When to use: Types define immutable data structures.
Examples: User, Order, Config, ApiResponse
Why type for data?
-
Emphasizes immutability
- Types with
readonlysignal "don't mutate this" - Functional programming alignment
- Types with
-
Better for unions, intersections, mapped types
type Result<T, E> = Success<T> | Failure<E>type Partial<T> = { [P in keyof T]?: T[P] }
-
Prevents accidental mutations
readonlyproperties enforce immutability at type level- Compiler catches mutation attempts
-
More flexible composition
- Easier to compose with utility types
- Better inference in complex scenarios
Example:
// Data structure
export type User = {
readonly id: string;
readonly email: string;
readonly name: string;
readonly roles: ReadonlyArray<string>;
};
export type Order = {
readonly id: string;
readonly userId: string;
readonly items: ReadonlyArray<OrderItem>;
readonly total: number;
};
Architectural Pattern
This pattern supports clean architecture:
- Behavior contracts (
interface) = Boundaries between layers - Data structures (
type) = Data flowing through the system - Business logic depends on interfaces, not implementations
- Data is immutable (types with
readonly)
Strict Mode Configuration
tsconfig.json Settings
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noPropertyAccessFromIndexSignature": true,
"forceConsistentCasingInFileNames": true,
"allowUnusedLabels": false
}
}
What Each Setting Does
Core strict flags:
strict: true- Enables all strict type checking optionsnoImplicitAny- Error on expressions/declarations with impliedanytypestrictNullChecks-nullandundefinedhave their own types (not assignable to everything)noUnusedLocals- Error on unused local variablesnoUnusedParameters- Error on unused function parametersnoImplicitReturns- Error when not all code paths return a valuenoFallthroughCasesInSwitch- Error on fallthrough cases in switch statements
Additional safety flags (CRITICAL):
noUncheckedIndexedAccess- Array/object access returnsT | undefined(prevents runtime errors from assuming elements exist)exactOptionalPropertyTypes- Distinguishesproperty?: Tfromproperty: T | undefined(more precise types)noPropertyAccessFromIndexSignature- Requires bracket notation for index signature properties (forces awareness of dynamic access)forceConsistentCasingInFileNames- Prevents case sensitivity issues across operating systemsallowUnusedLabels- Error on unused labels (catches accidental labels that do nothing)
Additional Rules
- No
@ts-ignorewithout explicit comments explaining why - These rules apply to test code as well as production code
Architectural Insight: noUnusedParameters Catches Design Issues
The noUnusedParameters rule can reveal architectural problems:
Example: A function with an unused parameter often indicates the parameter belongs in a different layer. Strict mode catches these design issues early.
Immutability Patterns
Use readonly on All Data Structures
// ✅ CORRECT - Immutable data structure
type ApiRequest = {
readonly method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
readonly url: string;
readonly headers?: {
readonly [key: string]: string;
};
readonly body?: unknown;
};
// ❌ WRONG - Mutable data structure
typ
---
*Content truncated.*
More by citypaul
View all skills by citypaul →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
pdf-to-markdown
aliceisjustplaying
Convert entire PDF documents to clean, structured Markdown for full context loading. Use this skill when the user wants to extract ALL text from a PDF into context (not grep/search), when discussing or analyzing PDF content in full, when the user mentions "load the whole PDF", "bring the PDF into context", "read the entire PDF", or when partial extraction/grepping would miss important context. This is the preferred method for PDF text extraction over page-by-page or grep approaches.
Related MCP Servers
Browse all serversCreate modern React UI components instantly with Magic AI Agent. Integrates with top IDEs for fast, stunning design and
Unlock seamless Salesforce org management with the secure, flexible Salesforce DX MCP Server. Streamline workflows and b
Quickly rp prototype web apps with Scaffold Generator: create consistent scaffolding using templates, variable substitut
Replicate Flux is an OpenAPI image generator using Replicate's Flux model, enabling image creation via API and TypeScrip
Integrate DuckDuckGo web search into your site with our MCP server, supporting features like Google custom search and ro
Memory Bank: A TypeScript server for persistent, markdown-based project memory management and session context tracking a
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.