跳到正文
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(原文)

安装前可以查看交给 AI 代理的指令内容。

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 / 1000 字

此表单受 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 的全部技能

报告此技能的问题