본문으로 건너뛰기
cccskills
무료GitHub에 공개

typescript-strict-migrator

Migrates TypeScript projects to strict mode incrementally with type guards, utility types, and best practices. Use when users request "TypeScript strict", "strict mode migration", "type safety", "strict TypeScript", or "ts-strict".

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md11.8 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

TypeScript Strict Migrator

Incrementally migrate to TypeScript strict mode for maximum type safety.

Core Workflow

  1. Audit current state: Check existing type errors
  2. Enable incrementally: One flag at a time
  3. Fix errors: Systematic approach per flag
  4. Add type guards: Runtime type checking
  5. Use utility types: Proper type transformations
  6. Document patterns: Team guidelines

Strict Mode Flags

// tsconfig.json - Full strict mode
{
  "compilerOptions": {
    // Master flag (enables all below)
    "strict": true,

    // Individual flags (enabled by strict)
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "useUnknownInCatchVariables": true,
    "alwaysStrict": true,

    // Additional strict-ish flags (not in strict)
    "noUncheckedIndexedAccess": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitOverride": true,
    "exactOptionalPropertyTypes": true,
    "noPropertyAccessFromIndexSignature": true
  }
}

Incremental Migration Strategy

Phase 1: Basic Strict Flags

// tsconfig.json - Phase 1
{
  "compilerOptions": {
    "strict": false,
    "noImplicitAny": true,
    "alwaysStrict": true
  }
}
// Before: implicit any
function processData(data) {
  return data.map(item => item.value);
}

// After: explicit types
function processData(data: DataItem[]): number[] {
  return data.map(item => item.value);
}

interface DataItem {
  value: number;
  label: string;
}

Phase 2: Strict Null Checks

// tsconfig.json - Phase 2
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}
// Before: potential null errors
function getUserName(user: User) {
  return user.profile.name;  // Error if profile is undefined
}

// After: proper null handling
function getUserName(user: User): string | undefined {
  return user.profile?.name;
}

// With non-null assertion (use sparingly)
function getUserNameOrThrow(user: User): string {
  if (!user.profile?.name) {
    throw new Error('User has no name');
  }
  return user.profile.name;
}

Phase 3: Function Types

// tsconfig.json - Phase 3
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true
  }
}
// Before: contravariance issues
type Handler = (event: Event) => void;
const mouseHandler: Handler = (event: MouseEvent) => {
  console.log(event.clientX);  // Error with strictFunctionTypes
};

// After: proper variance
type Handler<T extends Event = Event> = (event: T) => void;
const mouseHandler: Handler<MouseEvent> = (event) => {
  console.log(event.clientX);
};

Phase 4: Property Initialization

// tsconfig.json - Phase 4
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true
  }
}
// Before: uninitialized properties
class UserService {
  private apiClient: ApiClient;  // Error: not initialized

  constructor() {}
}

// After: definite assignment
class UserService {
  private apiClient: ApiClient;

  constructor(apiClient: ApiClient) {
    this.apiClient = apiClient;
  }
}

// Or with definite assignment assertion
class UserService {
  private apiClient!: ApiClient;  // Initialized in init()

  async init() {
    this.apiClient = await createApiClient();
  }
}

Type Guards

Basic Type Guards

// Type guard functions
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function isNumber(value: unknown): value is number {
  return typeof value === 'number';
}

function isObject(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null;
}

function isArray<T>(value: unknown, itemGuard: (item: unknown) => item is T): value is T[] {
  return Array.isArray(value) && value.every(itemGuard);
}

// Usage
function processInput(input: unknown) {
  if (isString(input)) {
    return input.toUpperCase();  // input is string
  }
  if (isNumber(input)) {
    return input.toFixed(2);  // input is number
  }
  throw new Error('Invalid input type');
}

Object Type Guards

interface User {
  id: string;
  name: string;
  email: string;
  role: 'admin' | 'user';
}

interface ApiResponse<T> {
  data: T;
  success: boolean;
}

// Type guard for User
function isUser(value: unknown): value is User {
  return (
    isObject(value) &&
    typeof value.id === 'string' &&
    typeof value.name === 'string' &&
    typeof value.email === 'string' &&
    (value.role === 'admin' || value.role === 'user')
  );
}

// Type guard for API response
function isApiResponse<T>(
  value: unknown,
  dataGuard: (data: unknown) => data is T
): value is ApiResponse<T> {
  return (
    isObject(value) &&
    typeof value.success === 'boolean' &&
    'data' in value &&
    dataGuard(value.data)
  );
}

// Usage
async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  const data: unknown = await response.json();

  if (!isApiResponse(data, isUser)) {
    throw new Error('Invalid API response');
  }

  return data.data;
}

Discriminated Unions

// Discriminated union pattern
type Result<T, E = Error> =
  | { success: true; data: T }
  | { success: false; error: E };

function createSuccess<T>(data: T): Result<T> {
  return { success: true, data };
}

function createError<E = Error>(error: E): Result<never, E> {
  return { success: false, error };
}

// Type guard via discriminant
function isSuccess<T, E>(result: Result<T, E>): result is { success: true; data: T } {
  return result.success === true;
}

// Usage
async function processRequest(): Promise<Result<User>> {
  try {
    const user = await fetchUser('123');
    return createSuccess(user);
  } catch (error) {
    return createError(error instanceof Error ? error : new Error(String(error)));
  }
}

const result = await processRequest();
if (isSuccess(result)) {
  console.log(result.data.name);  // TypeScript knows data exists
} else {
  console.error(result.error.message);  // TypeScript knows error exists
}

Utility Types for Migration

// Making properties required
type RequiredUser = Required<User>;

// Making properties optional
type PartialUser = Partial<User>;

// Pick specific properties
type UserCredentials = Pick<User, 'email' | 'id'>;

// Omit specific properties
type PublicUser = Omit<User, 'password' | 'internalId'>;

// Make properties readonly
type ReadonlyUser = Readonly<User>;

// Deep readonly
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object
    ? DeepReadonly<T[P]>
    : T[P];
};

// NonNullable
type DefiniteString = NonNullable<string | null | undefined>;  // string

// Extract and Exclude
type AdminRole = Extract<User['role'], 'admin'>;  // 'admin'
type NonAdminRole = Exclude<User['role'], 'admin'>;  // 'user'

// Record type
type UserById = Record<string, User>;

// Parameters and ReturnType
type FetchParams = Parameters<typeof fetch>;  // [input: RequestInfo, init?: RequestInit]
type FetchReturn = ReturnType<typeof fetch>;  // Promise<Response>

Common Migration Patterns

Handling Optional Chaining

// Before: unsafe access
const userName = user.profile.settings.displayName;

// After: safe access with optional chaining
const userName = user?.profile?.settings?.displayName;

// With nullish coalescing
const userName = user?.profile?.settings?.displayName ?? 'Anonymous';

// With type narrowing
function getDisplayName(user: User | null): string {
  if (!user?.profile?.settings?.displayName) {
    return 'Anonymous';
  }
  return user.profile.settings.displayName;
}

Assertion Functions

// Assertion function
function assertIsDefined<T>(value: T): asserts value is NonNullable<T> {
  if (value === undefined || value === null) {
    throw new Error('Value is not defined');
  }
}

function assertIsUser(value: unknown): asserts value is User {
  if (!isUser(value)) {
    throw new Error('Value is not a User');
  }
}

// Usage
function processUser(maybeUser: unknown) {
  assertIsUser(maybeUser);
  // maybeUser is now User
  console.log(maybeUser.name);
}

Error Handling

// Before: any in catch
try {
  await riskyOperation();
} catch (error) {
  console.error(error.message);  // Error with useUnknownInCatchVariables
}

// After: proper error handling
try {
  await riskyOperation();
} catch (error) {
  if (error instanceof Error) {
    console.error(error.message);
  } else {
    console.error('Unknown error:', String(error));
  }
}

// Helper function
function getErrorMessage(error: unknown): string {
  if (error instanceof Error) return error.message;
  if (typeof error === 'string') return error;
  return 'Unknown error occurred';
}

Index Signatures

// Before: unsafe index access
const users: Record<string, User> = {};
const user = users['unknown-id'];
console.log(user.name);  // Error with noUncheckedIndexedAccess

// After: proper null check
const user = users['unknown-id'];
if (user) {
  console.log(user.name);
}

// Or with assertion
const user = users['known-id']!;  // Only if you're certain

// Better: use Map
const usersMap = new Map<string, User>();
const user = usersMap.get('some-id');  // User | undefined by design

Migration Script

// scripts/analyze-strict.ts
import * as ts from 'typescript';
import * as path from 'path';

interface StrictAnalysis {
  noImplicitAny: number;
  strictNullChecks: number;
  strictFunctionTypes: number;
  strictPropertyInitialization: number;
  total: number;
}

function analyzeProject(configPath: string): StrictAnalysis {
  const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
  const parsedConfig = ts.parseJsonConfigFileContent(
    configFile.config,
    ts.sys,
    path.dirname(configPath)
  );

  // Enable strict flags one by one
  const strictOptions = {
    noImplicitAny: true,
    strictNullChecks: true,
    strictFunctionTypes: true,
    strictPropertyInitialization: true,
  };

  const analysis: StrictAnalysis = {
    noImplicitAny: 0,
    strictNullChecks: 0,
    strictFunctionTypes: 0,
    strictPropertyInitialization: 0,
    total: 0,
  };

  for (const [flag, _] of Object.entries(strictOptions)) {
    const options = {
      ...parsedConfig.options,
      [flag]: true,
    };

    const program = ts.createProgram(parsedConfig.fileNames, options);
    const diagnostics = ts.getPreEmitDiagnostics(program);

    analysis[flag as keyof StrictAnalysis] = diagnostics.length;
    analysis.total += diagnostics.length;
  }

  return analysis;
}

// Usage
const analysis = analyzeProject('./tsconfig.json');
console.log('Strict mode analysis:', analysis);

Best Practices

  1. Incremental adoption: One flag at a time
  2. Start with noImplicitAny: Easiest to fix
  3. Add type guards: Runtime safety
  4. Use assertion functions: Fail fast
  5. Avoid non-null assertions: Use sparingly
  6. Document patterns: Team consistency
  7. CI enforcement: Prevent regression
  8. Use unknown over any: Better type safety

Output Checklist

Every strict migration should include:

  • Baseline error count per flag
  • Migration plan with phases
  • Type guard utilities
  • Assertion functions
  • Error handling patterns
  • Index access handling
  • Optional chaining usage
  • Updated tsconfig.json
  • Team documentation
  • CI strict checking

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Audits and implements web accessibility (a11y) following WCAG 2.1 guidelines with ARIA patterns, keyboard navigation, screen reader support, and contrast checking. Use when users request "accessibility audit", "a11y review", "WCAG compliance", "screen reader support", or "keyboard navigation".

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

sathishssj3/Stereix-Engine22026년 10월 4일 업데이트

Creates Architecture Decision Records documenting key technical decisions with context, alternatives considered, tradeoffs, consequences, and decision owners. Use when documenting "architecture decisions", "technical choices", "design decisions", or "ADRs".

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

sathishssj3/Stereix-Engine22026년 10월 4일 업데이트

Designs multi-step agent workflows with tool usage, retry logic, state management, and budget controls. Provides orchestration diagrams, tool execution order, fallback strategies, and cost limits. Use for "AI agents", "agentic workflows", "multi-step AI", or "autonomous systems".

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

sathishssj3/Stereix-Engine22026년 10월 4일 업데이트

MUST USE when user wants to 调研/research/搜索/search/查/找/look up anything on the internet — e.g. 全网调研 X / 帮我调研一下 X / 查一下 X / 搜搜 X / 看看大家怎么评价 X / X 上有什么讨论 / research this topic。 Also MUST USE when user mentions any platform or shares any URL/链接: 小红书/xiaohongshu/xhs, Twitter/推特/X, B站/bilibili, Reddit, Facebook, Instagram, V2EX, LinkedIn/领英/招聘/求职/jobs, YouTube, GitHub code search, 小宇宙播客, 雪球/股票行情, RSS feeds, or any web URL. 15 platforms, multi-backend routing (OpenCLI / per-platform CLIs / APIs). Zero config for 6 channels. Run `agent-reach doctor --json` to see which backend serves each platform right now. NOT for: 写报告/数据分析/翻译等内容加工(本 skill 只负责从互联网获取内容); 发帖/评论/点赞等写操作;已有专门 skill 的平台(先用专门 skill)。 【路由方式】SKILL.md 包含路由表和常用命令,复杂场景需按需阅读对应分类的 references/*.md。 分类:search / social (小红书/推特/B站/V2EX/Reddit/Facebook/Instagram) / career(LinkedIn) / dev(github) / web(网页/文章/RSS) / video(YouTube/B站/播客) / finance(雪球/股票)。

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

sathishssj3/Stereix-Engine22026년 10월 4일 업데이트

Orchestrates multi-agent AI systems with task delegation, agent communication, shared memory, and workflow coordination. Use when users request "multi-agent system", "agent orchestration", "AI agents", "agent coordination", or "autonomous agents".

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

sathishssj3/Stereix-Engine22026년 10월 4일 업데이트

Creates SLO-based alerts and operational dashboards with key charts, alert thresholds, and runbook links. Use for "alerting", "dashboards", "SLO", or "monitoring".

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

sathishssj3/Stereix-Engine22026년 10월 4일 업데이트

sathishssj3의 스킬 모두 보기

이 스킬의 문제 신고