| name | TypeScript Ecosystem |
| description | This skill should be used when the user asks to "write typescript", "typescript config", "tsconfig", "type definition", "generics", "utility types", or works with TypeScript language patterns and configuration. Provides comprehensive TypeScript ecosystem patterns and best practices. |
| version | 0.1.0 |
Provide comprehensive patterns for TypeScript language, configuration, type system, and tooling integration.
Node.js version-specific recommended configurations
Current LTS - use ES2023 for stable features
Upcoming - use ES2024 for latest features
{
"compilerOptions": {
"target": "ES2023",
"lib": ["ES2023"],
"module": "nodenext",
"moduleResolution": "nodenext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
Node.js 22 LTS - use ES2023 target/lib for stable features
{
"compilerOptions": {
"target": "ES2024",
"lib": ["ES2024"],
"module": "nodenext",
"moduleResolution": "nodenext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
Node.js 24 (upcoming) - use ES2024 target/lib for latest features
<strict_options>
Enables all strict type-checking options
null and undefined handled explicitly
Stricter function type checking
Check bind, call, apply methods
Class properties must be initialized
Error on implicit any types
Error on missing return statements
Error on implicit this
Add undefined to index signatures
Error on unused local variables
Error on unused parameters
<module_resolution>
Modern Node.js ESM resolution (recommended)
{
"compilerOptions": {
"module": "nodenext",
"moduleResolution": "nodenext"
}
}
Requires "type": "module" in package.json
For projects using bundlers (Vite, esbuild, webpack)
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "bundler"
}
}
Import path aliases
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"]
}
}
}
baseUrl: deprecated in TS 6.0, removed in TS 7.0; prefer paths without baseUrl
moduleResolution: "node" (alias "node10"): deprecated in TS 5.x; use "nodenext" or "bundler"
<project_references>
Monorepo and incremental builds
{
"compilerOptions": {
"composite": true,
"incremental": true,
"tsBuildInfoFile": ".tsbuildinfo"
},
"references": [
{ "path": "../shared" },
{ "path": "../core" }
]
}
Use tsc --build for incremental compilation
</project_references>
<type_patterns>
<utility_types>
Make all properties optional
Make all properties required
Make all properties readonly
Object type with key K and value V
Select specific properties from T
Exclude specific properties from T
Exclude types from union
Extract types from union
Remove null and undefined
Get function return type
Get function parameter types as tuple
Unwrap Promise type
</utility_types>
Basic generic function
function identity<T>(arg: T): T {
return arg;
}
Generic with type constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
Generic with default type parameter
interface Container<T = string> {
value: T;
}
Multiple generic parameters with constraints
function merge<T extends object, U extends object>(a: T, b: U): T & U {
return { ...a, ...b };
}
<conditional_types>
Basic conditional type
type IsString<T> = T extends string ? true : false;
Extract types within conditional
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type ArrayElement<T> = T extends (infer E)[] ? E : never;
Distributes over union types
type ToArray<T> = T extends any ? T[] : never;
// ToArray<string | number> = string[] | number[]
<mapped_types>
Basic mapped type
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
Map keys with renaming
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
Filter properties by type
type OnlyStrings<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
<template_literal_types>
Template literal type construction
type EventName = on${Capitalize<string>};
type Locale = ${Language}-${Country};
Extract parameters from template literals
type ExtractRouteParams<T> = T extends `${string}:${infer Param}/${infer Rest}`
? Param | ExtractRouteParams<Rest>
: T extends `${string}:${infer Param}`
? Param
: never;
<type_guards>
Built-in typeof type guard
function process(value: string | number) {
if (typeof value === "string") {
return value.toUpperCase();
}
return value.toFixed(2);
}
Built-in instanceof type guard
function handle(error: Error | string) {
if (error instanceof Error) {
return error.message;
}
return error;
}
Custom type guard function
interface Cat { meow(): void; }
interface Dog { bark(): void; }
function isCat(pet: Cat | Dog): pet is Cat {
return (pet as Cat).meow !== undefined;
}
Property existence type guard
function move(animal: Fish | Bird) {
if ("swim" in animal) {
animal.swim();
} else {
animal.fly();
}
}
<branded_types>
Nominal typing via branding
type UserId = string & { readonly **brand: unique symbol };
type OrderId = string & { readonly **brand: unique symbol };
function createUserId(id: string): UserId {
return id as UserId;
}
Prevent mixing similar primitive types
</branded_types>
<satisfies_operator>
Type checking without widening
const config = {
endpoint: "/api",
timeout: 3000,
} satisfies Record<string, string | number>;
// config.endpoint is inferred as "/api" (literal), not string
</satisfies_operator>
</type_patterns>
<runtime_patterns>
<error_handling>
Rust-inspired Result type for error handling
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
function parseJson<T>(json: string): Result<T> {
try {
return { success: true, data: JSON.parse(json) };
} catch (e) {
return { success: false, error: e as Error };
}
}
Custom error classes with additional context
class ValidationError extends Error {
constructor(
message: string,
public readonly field: string,
public readonly code: string
) {
super(message);
this.name = "ValidationError";
}
}
Error chaining with cause (ES2022+)
try {
await fetchData();
} catch (e) {
throw new Error("Failed to fetch data", { cause: e });
}
<async_patterns>
Parallel promise execution
const [users, posts] = await Promise.all([
fetchUsers(),
fetchPosts(),
]);
Handle mixed success/failure
const results = await Promise.allSettled([
fetchUser(1),
fetchUser(2),
fetchUser(3),
]);
const successful = results
.filter((r): r is PromiseFulfilledResult<User> => r.status === "fulfilled")
.map((r) => r.value);
Async generator for pagination
async function* paginate<T>(fetchPage: (page: number) => Promise<T[]>) {
let page = 0;
while (true) {
const items = await fetchPage(page++);
if (items.length === 0) break;
yield* items;
}
}
for await (const item of paginate(fetchUsers)) {
console.log(item);
}
</async_patterns>
<module_patterns>
ES module export patterns
// Named exports
export const helper = () => {};
export type Config = { /* ... _/ };
// Default export
export default class Service {}
// Re-exports
export { util } from "./util.js";
export type { UtilOptions } from "./util.js";
index.ts for clean imports
// src/components/index.ts
export { Button } from "./Button.js";
export { Input } from "./Input.js";
export type { ButtonProps, InputProps } from "./types.js";
Can impact tree-shaking; use sparingly
Code splitting with dynamic imports
const module = await import("./heavy-module.js");
module.doSomething();
ESLint with TypeScript (flat config)
// eslint.config.js
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.strictTypeChecked,
{
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
}
);
import.meta.dirname requires Node.js 20.11+ or 21.2+ (not available in Node.js 18 LTS)
<key_rules>
Prefer unknown over any
Detect unused variables
Require explicit boolean conditions
Require awaiting promises
Use ?? over ||
</key_rules>
Prettier configuration for TypeScript
{
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100
}
Modern, fast test runner
// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
globals: true,
environment: "node",
coverage: {
provider: "v8",
reporter: ["text", "json", "html"],
},
},
});
Jest configuration for TypeScript
// jest.config.ts
import type { Config } from "jest";
const config: Config = {
preset: "ts-jest",
testEnvironment: "node",
roots: ["<rootDir>/src"],
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
},
};
export default config;
<build_tools>
TypeScript compiler commands
<use_case name="compile">tsc - Compile TypeScript</use_case>
<use_case name="build">tsc --build - Incremental build (monorepo)</use_case>
<use_case name="check">tsc --noEmit - Type check only</use_case>
<use_case name="watch">tsc --watch - Watch mode</use_case>
TypeScript execution with esbuild
tsx src/index.ts - Run TypeScript directly
tsx watch src/index.ts - Watch mode
Bundle TypeScript libraries
// tsup.config.ts
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/index.ts"],
format: ["cjs", "esm"],
dts: true,
clean: true,
sourcemap: true,
});
</build_tools>
<context7_integration>
<library_id>/microsoft/typescript</library_id>
<trust_score>9.9</trust_score>
16397
<usage_pattern>
Resolve library ID if needed (already known: /microsoft/typescript)
Fetch documentation with specific topic
Configuration options and patterns
Generic type patterns
Built-in utility types
Module resolution strategies
</usage_pattern>
<common_queries>
Strict compiler options
Path aliases configuration
Type declaration generation
ESM support in Node.js
</common_queries>
</context7_integration>
<anti_patterns>
Overusing 'any' defeats type safety
Use 'unknown' and narrow with type guards
Excessive 'as' casts bypass type checking
Use type guards or proper typing
Missing type annotations with noImplicitAny disabled
Enable strict mode, add explicit types
Barrel files can hurt tree-shaking
Use direct imports for large modules
Enums have runtime overhead and quirks
Use const objects with 'as const'
const Status = {
Active: "active",
Inactive: "inactive",
} as const;
type Status = (typeof Status)[keyof typeof Status];
Namespaces are legacy, use ES modules
Use regular imports/exports
Decorators add complexity and are still experimental
Prefer composition and higher-order functions
<best_practices>
Enable strict mode in all projects
Use noUncheckedIndexedAccess for safer array/object access
Prefer 'unknown' over 'any' for unknown types
Use 'satisfies' to check types without widening
Create branded types for domain primitives
Use Result types for error handling over exceptions
Keep type definitions close to usage
Export types separately with 'export type'
Use 'const' assertions for literal types
Prefer interfaces for public APIs, types for unions/utilities
</best_practices>