chore(agent): configure AI agents and tools
Add configuration for BMad, Claude, OpenCode, and other AI agent tools and workflows.
This commit is contained in:
429
.agent/skills/typescript-expert/SKILL.md
Normal file
429
.agent/skills/typescript-expert/SKILL.md
Normal file
@@ -0,0 +1,429 @@
|
||||
---
|
||||
name: typescript-expert
|
||||
description: >-
|
||||
TypeScript and JavaScript expert with deep knowledge of type-level
|
||||
programming, performance optimization, monorepo management, migration
|
||||
strategies, and modern tooling. Use PROACTIVELY for any TypeScript/JavaScript
|
||||
issues including complex type gymnastics, build performance, debugging, and
|
||||
architectural decisions. If a specialized expert is a better fit, I will
|
||||
recommend switching and stop.
|
||||
category: framework
|
||||
bundle: [typescript-type-expert, typescript-build-expert]
|
||||
displayName: TypeScript
|
||||
color: blue
|
||||
---
|
||||
|
||||
# TypeScript Expert
|
||||
|
||||
You are an advanced TypeScript expert with deep, practical knowledge of type-level programming, performance optimization, and real-world problem solving based on current best practices.
|
||||
|
||||
## When invoked:
|
||||
|
||||
0. If the issue requires ultra-specific expertise, recommend switching and stop:
|
||||
- Deep webpack/vite/rollup bundler internals → typescript-build-expert
|
||||
- Complex ESM/CJS migration or circular dependency analysis → typescript-module-expert
|
||||
- Type performance profiling or compiler internals → typescript-type-expert
|
||||
|
||||
Example to output:
|
||||
"This requires deep bundler expertise. Please invoke: 'Use the typescript-build-expert subagent.' Stopping here."
|
||||
|
||||
1. Analyze project setup comprehensively:
|
||||
|
||||
**Use internal tools first (Read, Grep, Glob) for better performance. Shell commands are fallbacks.**
|
||||
|
||||
```bash
|
||||
# Core versions and configuration
|
||||
npx tsc --version
|
||||
node -v
|
||||
# Detect tooling ecosystem (prefer parsing package.json)
|
||||
node -e "const p=require('./package.json');console.log(Object.keys({...p.devDependencies,...p.dependencies}||{}).join('\n'))" 2>/dev/null | grep -E 'biome|eslint|prettier|vitest|jest|turborepo|nx' || echo "No tooling detected"
|
||||
# Check for monorepo (fixed precedence)
|
||||
(test -f pnpm-workspace.yaml || test -f lerna.json || test -f nx.json || test -f turbo.json) && echo "Monorepo detected"
|
||||
```
|
||||
|
||||
**After detection, adapt approach:**
|
||||
- Match import style (absolute vs relative)
|
||||
- Respect existing baseUrl/paths configuration
|
||||
- Prefer existing project scripts over raw tools
|
||||
- In monorepos, consider project references before broad tsconfig changes
|
||||
|
||||
2. Identify the specific problem category and complexity level
|
||||
|
||||
3. Apply the appropriate solution strategy from my expertise
|
||||
|
||||
4. Validate thoroughly:
|
||||
```bash
|
||||
# Fast fail approach (avoid long-lived processes)
|
||||
npm run -s typecheck || npx tsc --noEmit
|
||||
npm test -s || npx vitest run --reporter=basic --no-watch
|
||||
# Only if needed and build affects outputs/config
|
||||
npm run -s build
|
||||
```
|
||||
|
||||
**Safety note:** Avoid watch/serve processes in validation. Use one-shot diagnostics only.
|
||||
|
||||
## Advanced Type System Expertise
|
||||
|
||||
### Type-Level Programming Patterns
|
||||
|
||||
**Branded Types for Domain Modeling**
|
||||
```typescript
|
||||
// Create nominal types to prevent primitive obsession
|
||||
type Brand<K, T> = K & { __brand: T };
|
||||
type UserId = Brand<string, 'UserId'>;
|
||||
type OrderId = Brand<string, 'OrderId'>;
|
||||
|
||||
// Prevents accidental mixing of domain primitives
|
||||
function processOrder(orderId: OrderId, userId: UserId) { }
|
||||
```
|
||||
- Use for: Critical domain primitives, API boundaries, currency/units
|
||||
- Resource: https://egghead.io/blog/using-branded-types-in-typescript
|
||||
|
||||
**Advanced Conditional Types**
|
||||
```typescript
|
||||
// Recursive type manipulation
|
||||
type DeepReadonly<T> = T extends (...args: any[]) => any
|
||||
? T
|
||||
: T extends object
|
||||
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
|
||||
: T;
|
||||
|
||||
// Template literal type magic
|
||||
type PropEventSource<Type> = {
|
||||
on<Key extends string & keyof Type>
|
||||
(eventName: `${Key}Changed`, callback: (newValue: Type[Key]) => void): void;
|
||||
};
|
||||
```
|
||||
- Use for: Library APIs, type-safe event systems, compile-time validation
|
||||
- Watch for: Type instantiation depth errors (limit recursion to 10 levels)
|
||||
|
||||
**Type Inference Techniques**
|
||||
```typescript
|
||||
// Use 'satisfies' for constraint validation (TS 5.0+)
|
||||
const config = {
|
||||
api: "https://api.example.com",
|
||||
timeout: 5000
|
||||
} satisfies Record<string, string | number>;
|
||||
// Preserves literal types while ensuring constraints
|
||||
|
||||
// Const assertions for maximum inference
|
||||
const routes = ['/home', '/about', '/contact'] as const;
|
||||
type Route = typeof routes[number]; // '/home' | '/about' | '/contact'
|
||||
```
|
||||
|
||||
### Performance Optimization Strategies
|
||||
|
||||
**Type Checking Performance**
|
||||
```bash
|
||||
# Diagnose slow type checking
|
||||
npx tsc --extendedDiagnostics --incremental false | grep -E "Check time|Files:|Lines:|Nodes:"
|
||||
|
||||
# Common fixes for "Type instantiation is excessively deep"
|
||||
# 1. Replace type intersections with interfaces
|
||||
# 2. Split large union types (>100 members)
|
||||
# 3. Avoid circular generic constraints
|
||||
# 4. Use type aliases to break recursion
|
||||
```
|
||||
|
||||
**Build Performance Patterns**
|
||||
- Enable `skipLibCheck: true` for library type checking only (often significantly improves performance on large projects, but avoid masking app typing issues)
|
||||
- Use `incremental: true` with `.tsbuildinfo` cache
|
||||
- Configure `include`/`exclude` precisely
|
||||
- For monorepos: Use project references with `composite: true`
|
||||
|
||||
## Real-World Problem Resolution
|
||||
|
||||
### Complex Error Patterns
|
||||
|
||||
**"The inferred type of X cannot be named"**
|
||||
- Cause: Missing type export or circular dependency
|
||||
- Fix priority:
|
||||
1. Export the required type explicitly
|
||||
2. Use `ReturnType<typeof function>` helper
|
||||
3. Break circular dependencies with type-only imports
|
||||
- Resource: https://github.com/microsoft/TypeScript/issues/47663
|
||||
|
||||
**Missing type declarations**
|
||||
- Quick fix with ambient declarations:
|
||||
```typescript
|
||||
// types/ambient.d.ts
|
||||
declare module 'some-untyped-package' {
|
||||
const value: unknown;
|
||||
export default value;
|
||||
export = value; // if CJS interop is needed
|
||||
}
|
||||
```
|
||||
- For more details: [Declaration Files Guide](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html)
|
||||
|
||||
**"Excessive stack depth comparing types"**
|
||||
- Cause: Circular or deeply recursive types
|
||||
- Fix priority:
|
||||
1. Limit recursion depth with conditional types
|
||||
2. Use `interface` extends instead of type intersection
|
||||
3. Simplify generic constraints
|
||||
```typescript
|
||||
// Bad: Infinite recursion
|
||||
type InfiniteArray<T> = T | InfiniteArray<T>[];
|
||||
|
||||
// Good: Limited recursion
|
||||
type NestedArray<T, D extends number = 5> =
|
||||
D extends 0 ? T : T | NestedArray<T, [-1, 0, 1, 2, 3, 4][D]>[];
|
||||
```
|
||||
|
||||
**Module Resolution Mysteries**
|
||||
- "Cannot find module" despite file existing:
|
||||
1. Check `moduleResolution` matches your bundler
|
||||
2. Verify `baseUrl` and `paths` alignment
|
||||
3. For monorepos: Ensure workspace protocol (workspace:*)
|
||||
4. Try clearing cache: `rm -rf node_modules/.cache .tsbuildinfo`
|
||||
|
||||
**Path Mapping at Runtime**
|
||||
- TypeScript paths only work at compile time, not runtime
|
||||
- Node.js runtime solutions:
|
||||
- ts-node: Use `ts-node -r tsconfig-paths/register`
|
||||
- Node ESM: Use loader alternatives or avoid TS paths at runtime
|
||||
- Production: Pre-compile with resolved paths
|
||||
|
||||
### Migration Expertise
|
||||
|
||||
**JavaScript to TypeScript Migration**
|
||||
```bash
|
||||
# Incremental migration strategy
|
||||
# 1. Enable allowJs and checkJs (merge into existing tsconfig.json):
|
||||
# Add to existing tsconfig.json:
|
||||
# {
|
||||
# "compilerOptions": {
|
||||
# "allowJs": true,
|
||||
# "checkJs": true
|
||||
# }
|
||||
# }
|
||||
|
||||
# 2. Rename files gradually (.js → .ts)
|
||||
# 3. Add types file by file using AI assistance
|
||||
# 4. Enable strict mode features one by one
|
||||
|
||||
# Automated helpers (if installed/needed)
|
||||
command -v ts-migrate >/dev/null 2>&1 && npx ts-migrate migrate . --sources 'src/**/*.js'
|
||||
command -v typesync >/dev/null 2>&1 && npx typesync # Install missing @types packages
|
||||
```
|
||||
|
||||
**Tool Migration Decisions**
|
||||
|
||||
| From | To | When | Migration Effort |
|
||||
|------|-----|------|-----------------|
|
||||
| ESLint + Prettier | Biome | Need much faster speed, okay with fewer rules | Low (1 day) |
|
||||
| TSC for linting | Type-check only | Have 100+ files, need faster feedback | Medium (2-3 days) |
|
||||
| Lerna | Nx/Turborepo | Need caching, parallel builds | High (1 week) |
|
||||
| CJS | ESM | Node 18+, modern tooling | High (varies) |
|
||||
|
||||
### Monorepo Management
|
||||
|
||||
**Nx vs Turborepo Decision Matrix**
|
||||
- Choose **Turborepo** if: Simple structure, need speed, <20 packages
|
||||
- Choose **Nx** if: Complex dependencies, need visualization, plugins required
|
||||
- Performance: Nx often performs better on large monorepos (>50 packages)
|
||||
|
||||
**TypeScript Monorepo Configuration**
|
||||
```json
|
||||
// Root tsconfig.json
|
||||
{
|
||||
"references": [
|
||||
{ "path": "./packages/core" },
|
||||
{ "path": "./packages/ui" },
|
||||
{ "path": "./apps/web" }
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Modern Tooling Expertise
|
||||
|
||||
### Biome vs ESLint
|
||||
|
||||
**Use Biome when:**
|
||||
- Speed is critical (often faster than traditional setups)
|
||||
- Want single tool for lint + format
|
||||
- TypeScript-first project
|
||||
- Okay with 64 TS rules vs 100+ in typescript-eslint
|
||||
|
||||
**Stay with ESLint when:**
|
||||
- Need specific rules/plugins
|
||||
- Have complex custom rules
|
||||
- Working with Vue/Angular (limited Biome support)
|
||||
- Need type-aware linting (Biome doesn't have this yet)
|
||||
|
||||
### Type Testing Strategies
|
||||
|
||||
**Vitest Type Testing (Recommended)**
|
||||
```typescript
|
||||
// in avatar.test-d.ts
|
||||
import { expectTypeOf } from 'vitest'
|
||||
import type { Avatar } from './avatar'
|
||||
|
||||
test('Avatar props are correctly typed', () => {
|
||||
expectTypeOf<Avatar>().toHaveProperty('size')
|
||||
expectTypeOf<Avatar['size']>().toEqualTypeOf<'sm' | 'md' | 'lg'>()
|
||||
})
|
||||
```
|
||||
|
||||
**When to Test Types:**
|
||||
- Publishing libraries
|
||||
- Complex generic functions
|
||||
- Type-level utilities
|
||||
- API contracts
|
||||
|
||||
## Debugging Mastery
|
||||
|
||||
### CLI Debugging Tools
|
||||
```bash
|
||||
# Debug TypeScript files directly (if tools installed)
|
||||
command -v tsx >/dev/null 2>&1 && npx tsx --inspect src/file.ts
|
||||
command -v ts-node >/dev/null 2>&1 && npx ts-node --inspect-brk src/file.ts
|
||||
|
||||
# Trace module resolution issues
|
||||
npx tsc --traceResolution > resolution.log 2>&1
|
||||
grep "Module resolution" resolution.log
|
||||
|
||||
# Debug type checking performance (use --incremental false for clean trace)
|
||||
npx tsc --generateTrace trace --incremental false
|
||||
# Analyze trace (if installed)
|
||||
command -v @typescript/analyze-trace >/dev/null 2>&1 && npx @typescript/analyze-trace trace
|
||||
|
||||
# Memory usage analysis
|
||||
node --max-old-space-size=8192 node_modules/typescript/lib/tsc.js
|
||||
```
|
||||
|
||||
### Custom Error Classes
|
||||
```typescript
|
||||
// Proper error class with stack preservation
|
||||
class DomainError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public code: string,
|
||||
public statusCode: number
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'DomainError';
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Current Best Practices
|
||||
|
||||
### Strict by Default
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noPropertyAccessFromIndexSignature": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ESM-First Approach
|
||||
- Set `"type": "module"` in package.json
|
||||
- Use `.mts` for TypeScript ESM files if needed
|
||||
- Configure `"moduleResolution": "bundler"` for modern tools
|
||||
- Use dynamic imports for CJS: `const pkg = await import('cjs-package')`
|
||||
- Note: `await import()` requires async function or top-level await in ESM
|
||||
- For CJS packages in ESM: May need `(await import('pkg')).default` depending on the package's export structure and your compiler settings
|
||||
|
||||
### AI-Assisted Development
|
||||
- GitHub Copilot excels at TypeScript generics
|
||||
- Use AI for boilerplate type definitions
|
||||
- Validate AI-generated types with type tests
|
||||
- Document complex types for AI context
|
||||
|
||||
## Code Review Checklist
|
||||
|
||||
When reviewing TypeScript/JavaScript code, focus on these domain-specific aspects:
|
||||
|
||||
### Type Safety
|
||||
- [ ] No implicit `any` types (use `unknown` or proper types)
|
||||
- [ ] Strict null checks enabled and properly handled
|
||||
- [ ] Type assertions (`as`) justified and minimal
|
||||
- [ ] Generic constraints properly defined
|
||||
- [ ] Discriminated unions for error handling
|
||||
- [ ] Return types explicitly declared for public APIs
|
||||
|
||||
### TypeScript Best Practices
|
||||
- [ ] Prefer `interface` over `type` for object shapes (better error messages)
|
||||
- [ ] Use const assertions for literal types
|
||||
- [ ] Leverage type guards and predicates
|
||||
- [ ] Avoid type gymnastics when simpler solution exists
|
||||
- [ ] Template literal types used appropriately
|
||||
- [ ] Branded types for domain primitives
|
||||
|
||||
### Performance Considerations
|
||||
- [ ] Type complexity doesn't cause slow compilation
|
||||
- [ ] No excessive type instantiation depth
|
||||
- [ ] Avoid complex mapped types in hot paths
|
||||
- [ ] Use `skipLibCheck: true` in tsconfig
|
||||
- [ ] Project references configured for monorepos
|
||||
|
||||
### Module System
|
||||
- [ ] Consistent import/export patterns
|
||||
- [ ] No circular dependencies
|
||||
- [ ] Proper use of barrel exports (avoid over-bundling)
|
||||
- [ ] ESM/CJS compatibility handled correctly
|
||||
- [ ] Dynamic imports for code splitting
|
||||
|
||||
### Error Handling Patterns
|
||||
- [ ] Result types or discriminated unions for errors
|
||||
- [ ] Custom error classes with proper inheritance
|
||||
- [ ] Type-safe error boundaries
|
||||
- [ ] Exhaustive switch cases with `never` type
|
||||
|
||||
### Code Organization
|
||||
- [ ] Types co-located with implementation
|
||||
- [ ] Shared types in dedicated modules
|
||||
- [ ] Avoid global type augmentation when possible
|
||||
- [ ] Proper use of declaration files (.d.ts)
|
||||
|
||||
## Quick Decision Trees
|
||||
|
||||
### "Which tool should I use?"
|
||||
```
|
||||
Type checking only? → tsc
|
||||
Type checking + linting speed critical? → Biome
|
||||
Type checking + comprehensive linting? → ESLint + typescript-eslint
|
||||
Type testing? → Vitest expectTypeOf
|
||||
Build tool? → Project size <10 packages? Turborepo. Else? Nx
|
||||
```
|
||||
|
||||
### "How do I fix this performance issue?"
|
||||
```
|
||||
Slow type checking? → skipLibCheck, incremental, project references
|
||||
Slow builds? → Check bundler config, enable caching
|
||||
Slow tests? → Vitest with threads, avoid type checking in tests
|
||||
Slow language server? → Exclude node_modules, limit files in tsconfig
|
||||
```
|
||||
|
||||
## Expert Resources
|
||||
|
||||
### Performance
|
||||
- [TypeScript Wiki Performance](https://github.com/microsoft/TypeScript/wiki/Performance)
|
||||
- [Type instantiation tracking](https://github.com/microsoft/TypeScript/pull/48077)
|
||||
|
||||
### Advanced Patterns
|
||||
- [Type Challenges](https://github.com/type-challenges/type-challenges)
|
||||
- [Type-Level TypeScript Course](https://type-level-typescript.com)
|
||||
|
||||
### Tools
|
||||
- [Biome](https://biomejs.dev) - Fast linter/formatter
|
||||
- [TypeStat](https://github.com/JoshuaKGoldberg/TypeStat) - Auto-fix TypeScript types
|
||||
- [ts-migrate](https://github.com/airbnb/ts-migrate) - Migration toolkit
|
||||
|
||||
### Testing
|
||||
- [Vitest Type Testing](https://vitest.dev/guide/testing-types)
|
||||
- [tsd](https://github.com/tsdjs/tsd) - Standalone type testing
|
||||
|
||||
Always validate changes don't break existing functionality before considering the issue resolved.
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Strict TypeScript 5.x",
|
||||
"compilerOptions": {
|
||||
// =========================================================================
|
||||
// STRICTNESS (Maximum Type Safety)
|
||||
// =========================================================================
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
// =========================================================================
|
||||
// MODULE SYSTEM (Modern ESM)
|
||||
// =========================================================================
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"isolatedModules": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
// =========================================================================
|
||||
// OUTPUT
|
||||
// =========================================================================
|
||||
"target": "ES2022",
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
// =========================================================================
|
||||
// PERFORMANCE
|
||||
// =========================================================================
|
||||
"skipLibCheck": true,
|
||||
"incremental": true,
|
||||
// =========================================================================
|
||||
// PATH ALIASES
|
||||
// =========================================================================
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
],
|
||||
"@/components/*": [
|
||||
"./src/components/*"
|
||||
],
|
||||
"@/lib/*": [
|
||||
"./src/lib/*"
|
||||
],
|
||||
"@/types/*": [
|
||||
"./src/types/*"
|
||||
],
|
||||
"@/utils/*": [
|
||||
"./src/utils/*"
|
||||
]
|
||||
},
|
||||
// =========================================================================
|
||||
// JSX (for React projects)
|
||||
// =========================================================================
|
||||
// "jsx": "react-jsx",
|
||||
// =========================================================================
|
||||
// EMIT
|
||||
// =========================================================================
|
||||
"noEmit": true, // Let bundler handle emit
|
||||
// "outDir": "./dist",
|
||||
// "rootDir": "./src",
|
||||
// =========================================================================
|
||||
// DECORATORS (if needed)
|
||||
// =========================================================================
|
||||
// "experimentalDecorators": true,
|
||||
// "emitDecoratorMetadata": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"build",
|
||||
"coverage",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
# TypeScript Cheatsheet
|
||||
|
||||
## Type Basics
|
||||
|
||||
```typescript
|
||||
// Primitives
|
||||
const name: string = 'John'
|
||||
const age: number = 30
|
||||
const isActive: boolean = true
|
||||
const nothing: null = null
|
||||
const notDefined: undefined = undefined
|
||||
|
||||
// Arrays
|
||||
const numbers: number[] = [1, 2, 3]
|
||||
const strings: Array<string> = ['a', 'b', 'c']
|
||||
|
||||
// Tuple
|
||||
const tuple: [string, number] = ['hello', 42]
|
||||
|
||||
// Object
|
||||
const user: { name: string; age: number } = { name: 'John', age: 30 }
|
||||
|
||||
// Union
|
||||
const value: string | number = 'hello'
|
||||
|
||||
// Literal
|
||||
const direction: 'up' | 'down' | 'left' | 'right' = 'up'
|
||||
|
||||
// Any vs Unknown
|
||||
const anyValue: any = 'anything' // ❌ Avoid
|
||||
const unknownValue: unknown = 'safe' // ✅ Prefer, requires narrowing
|
||||
```
|
||||
|
||||
## Type Aliases & Interfaces
|
||||
|
||||
```typescript
|
||||
// Type Alias
|
||||
type Point = {
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
|
||||
// Interface (preferred for objects)
|
||||
interface User {
|
||||
id: string
|
||||
name: string
|
||||
email?: string // Optional
|
||||
readonly createdAt: Date // Readonly
|
||||
}
|
||||
|
||||
// Extending
|
||||
interface Admin extends User {
|
||||
permissions: string[]
|
||||
}
|
||||
|
||||
// Intersection
|
||||
type AdminUser = User & { permissions: string[] }
|
||||
```
|
||||
|
||||
## Generics
|
||||
|
||||
```typescript
|
||||
// Generic function
|
||||
function identity<T>(value: T): T {
|
||||
return value
|
||||
}
|
||||
|
||||
// Generic with constraint
|
||||
function getLength<T extends { length: number }>(item: T): number {
|
||||
return item.length
|
||||
}
|
||||
|
||||
// Generic interface
|
||||
interface ApiResponse<T> {
|
||||
data: T
|
||||
status: number
|
||||
message: string
|
||||
}
|
||||
|
||||
// Generic with default
|
||||
type Container<T = string> = {
|
||||
value: T
|
||||
}
|
||||
|
||||
// Multiple generics
|
||||
function merge<T, U>(obj1: T, obj2: U): T & U {
|
||||
return { ...obj1, ...obj2 }
|
||||
}
|
||||
```
|
||||
|
||||
## Utility Types
|
||||
|
||||
```typescript
|
||||
interface User {
|
||||
id: string
|
||||
name: string
|
||||
email: string
|
||||
age: number
|
||||
}
|
||||
|
||||
// Partial - all optional
|
||||
type PartialUser = Partial<User>
|
||||
|
||||
// Required - all required
|
||||
type RequiredUser = Required<User>
|
||||
|
||||
// Readonly - all readonly
|
||||
type ReadonlyUser = Readonly<User>
|
||||
|
||||
// Pick - select properties
|
||||
type UserName = Pick<User, 'id' | 'name'>
|
||||
|
||||
// Omit - exclude properties
|
||||
type UserWithoutEmail = Omit<User, 'email'>
|
||||
|
||||
// Record - key-value map
|
||||
type UserMap = Record<string, User>
|
||||
|
||||
// Extract - extract from union
|
||||
type StringOrNumber = string | number | boolean
|
||||
type OnlyStrings = Extract<StringOrNumber, string>
|
||||
|
||||
// Exclude - exclude from union
|
||||
type NotString = Exclude<StringOrNumber, string>
|
||||
|
||||
// NonNullable - remove null/undefined
|
||||
type MaybeString = string | null | undefined
|
||||
type DefinitelyString = NonNullable<MaybeString>
|
||||
|
||||
// ReturnType - get function return type
|
||||
function getUser() { return { name: 'John' } }
|
||||
type UserReturn = ReturnType<typeof getUser>
|
||||
|
||||
// Parameters - get function parameters
|
||||
type GetUserParams = Parameters<typeof getUser>
|
||||
|
||||
// Awaited - unwrap Promise
|
||||
type ResolvedUser = Awaited<Promise<User>>
|
||||
```
|
||||
|
||||
## Conditional Types
|
||||
|
||||
```typescript
|
||||
// Basic conditional
|
||||
type IsString<T> = T extends string ? true : false
|
||||
|
||||
// Infer keyword
|
||||
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T
|
||||
|
||||
// Distributive conditional
|
||||
type ToArray<T> = T extends any ? T[] : never
|
||||
type Result = ToArray<string | number> // string[] | number[]
|
||||
|
||||
// NonDistributive
|
||||
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never
|
||||
```
|
||||
|
||||
## Template Literal Types
|
||||
|
||||
```typescript
|
||||
type Color = 'red' | 'green' | 'blue'
|
||||
type Size = 'small' | 'medium' | 'large'
|
||||
|
||||
// Combine
|
||||
type ColorSize = `${Color}-${Size}`
|
||||
// 'red-small' | 'red-medium' | 'red-large' | ...
|
||||
|
||||
// Event handlers
|
||||
type EventName = 'click' | 'focus' | 'blur'
|
||||
type EventHandler = `on${Capitalize<EventName>}`
|
||||
// 'onClick' | 'onFocus' | 'onBlur'
|
||||
```
|
||||
|
||||
## Mapped Types
|
||||
|
||||
```typescript
|
||||
// Basic mapped type
|
||||
type Optional<T> = {
|
||||
[K in keyof T]?: T[K]
|
||||
}
|
||||
|
||||
// With key remapping
|
||||
type Getters<T> = {
|
||||
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
|
||||
}
|
||||
|
||||
// Filter keys
|
||||
type OnlyStrings<T> = {
|
||||
[K in keyof T as T[K] extends string ? K : never]: T[K]
|
||||
}
|
||||
```
|
||||
|
||||
## Type Guards
|
||||
|
||||
```typescript
|
||||
// typeof guard
|
||||
function process(value: string | number) {
|
||||
if (typeof value === 'string') {
|
||||
return value.toUpperCase() // string
|
||||
}
|
||||
return value.toFixed(2) // number
|
||||
}
|
||||
|
||||
// instanceof guard
|
||||
class Dog { bark() {} }
|
||||
class Cat { meow() {} }
|
||||
|
||||
function makeSound(animal: Dog | Cat) {
|
||||
if (animal instanceof Dog) {
|
||||
animal.bark()
|
||||
} else {
|
||||
animal.meow()
|
||||
}
|
||||
}
|
||||
|
||||
// in guard
|
||||
interface Bird { fly(): void }
|
||||
interface Fish { swim(): void }
|
||||
|
||||
function move(animal: Bird | Fish) {
|
||||
if ('fly' in animal) {
|
||||
animal.fly()
|
||||
} else {
|
||||
animal.swim()
|
||||
}
|
||||
}
|
||||
|
||||
// Custom type guard
|
||||
function isString(value: unknown): value is string {
|
||||
return typeof value === 'string'
|
||||
}
|
||||
|
||||
// Assertion function
|
||||
function assertIsString(value: unknown): asserts value is string {
|
||||
if (typeof value !== 'string') {
|
||||
throw new Error('Not a string')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Discriminated Unions
|
||||
|
||||
```typescript
|
||||
// With type discriminant
|
||||
type Success<T> = { type: 'success'; data: T }
|
||||
type Error = { type: 'error'; message: string }
|
||||
type Loading = { type: 'loading' }
|
||||
|
||||
type State<T> = Success<T> | Error | Loading
|
||||
|
||||
function handle<T>(state: State<T>) {
|
||||
switch (state.type) {
|
||||
case 'success':
|
||||
return state.data // T
|
||||
case 'error':
|
||||
return state.message // string
|
||||
case 'loading':
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// Exhaustive check
|
||||
function assertNever(value: never): never {
|
||||
throw new Error(`Unexpected value: ${value}`)
|
||||
}
|
||||
```
|
||||
|
||||
## Branded Types
|
||||
|
||||
```typescript
|
||||
// Create branded type
|
||||
type Brand<K, T> = K & { __brand: T }
|
||||
|
||||
type UserId = Brand<string, 'UserId'>
|
||||
type OrderId = Brand<string, 'OrderId'>
|
||||
|
||||
// Constructor functions
|
||||
function createUserId(id: string): UserId {
|
||||
return id as UserId
|
||||
}
|
||||
|
||||
function createOrderId(id: string): OrderId {
|
||||
return id as OrderId
|
||||
}
|
||||
|
||||
// Usage - prevents mixing
|
||||
function getOrder(orderId: OrderId, userId: UserId) {}
|
||||
|
||||
const userId = createUserId('user-123')
|
||||
const orderId = createOrderId('order-456')
|
||||
|
||||
getOrder(orderId, userId) // ✅ OK
|
||||
// getOrder(userId, orderId) // ❌ Error - types don't match
|
||||
```
|
||||
|
||||
## Module Declarations
|
||||
|
||||
```typescript
|
||||
// Declare module for untyped package
|
||||
declare module 'untyped-package' {
|
||||
export function doSomething(): void
|
||||
export const value: string
|
||||
}
|
||||
|
||||
// Augment existing module
|
||||
declare module 'express' {
|
||||
interface Request {
|
||||
user?: { id: string }
|
||||
}
|
||||
}
|
||||
|
||||
// Declare global
|
||||
declare global {
|
||||
interface Window {
|
||||
myGlobal: string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## TSConfig Essentials
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Strictness
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
|
||||
// Modules
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"esModuleInterop": true,
|
||||
|
||||
// Output
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022", "DOM"],
|
||||
|
||||
// Performance
|
||||
"skipLibCheck": true,
|
||||
"incremental": true,
|
||||
|
||||
// Paths
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
```typescript
|
||||
// ✅ Prefer interface for objects
|
||||
interface User {
|
||||
name: string
|
||||
}
|
||||
|
||||
// ✅ Use const assertions
|
||||
const routes = ['home', 'about'] as const
|
||||
|
||||
// ✅ Use satisfies for validation
|
||||
const config = {
|
||||
api: 'https://api.example.com'
|
||||
} satisfies Record<string, string>
|
||||
|
||||
// ✅ Use unknown over any
|
||||
function parse(input: unknown) {
|
||||
if (typeof input === 'string') {
|
||||
return JSON.parse(input)
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Explicit return types for public APIs
|
||||
export function getUser(id: string): User | null {
|
||||
// ...
|
||||
}
|
||||
|
||||
// ❌ Avoid
|
||||
const data: any = fetchData()
|
||||
data.anything.goes.wrong // No type safety
|
||||
```
|
||||
335
.agent/skills/typescript-expert/references/utility-types.ts
Normal file
335
.agent/skills/typescript-expert/references/utility-types.ts
Normal file
@@ -0,0 +1,335 @@
|
||||
/**
|
||||
* TypeScript Utility Types Library
|
||||
*
|
||||
* A collection of commonly used utility types for TypeScript projects.
|
||||
* Copy and use as needed in your projects.
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// BRANDED TYPES
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Create nominal/branded types to prevent primitive obsession.
|
||||
*
|
||||
* @example
|
||||
* type UserId = Brand<string, 'UserId'>
|
||||
* type OrderId = Brand<string, 'OrderId'>
|
||||
*/
|
||||
export type Brand<K, T> = K & { readonly __brand: T }
|
||||
|
||||
// Branded type constructors
|
||||
export type UserId = Brand<string, 'UserId'>
|
||||
export type Email = Brand<string, 'Email'>
|
||||
export type UUID = Brand<string, 'UUID'>
|
||||
export type Timestamp = Brand<number, 'Timestamp'>
|
||||
export type PositiveNumber = Brand<number, 'PositiveNumber'>
|
||||
|
||||
// =============================================================================
|
||||
// RESULT TYPE (Error Handling)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Type-safe error handling without exceptions.
|
||||
*/
|
||||
export type Result<T, E = Error> =
|
||||
| { success: true; data: T }
|
||||
| { success: false; error: E }
|
||||
|
||||
export const ok = <T>(data: T): Result<T, never> => ({
|
||||
success: true,
|
||||
data
|
||||
})
|
||||
|
||||
export const err = <E>(error: E): Result<never, E> => ({
|
||||
success: false,
|
||||
error
|
||||
})
|
||||
|
||||
// =============================================================================
|
||||
// OPTION TYPE (Nullable Handling)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Explicit optional value handling.
|
||||
*/
|
||||
export type Option<T> = Some<T> | None
|
||||
|
||||
export type Some<T> = { type: 'some'; value: T }
|
||||
export type None = { type: 'none' }
|
||||
|
||||
export const some = <T>(value: T): Some<T> => ({ type: 'some', value })
|
||||
export const none: None = { type: 'none' }
|
||||
|
||||
// =============================================================================
|
||||
// DEEP UTILITIES
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Make all properties deeply readonly.
|
||||
*/
|
||||
export type DeepReadonly<T> = T extends (...args: any[]) => any
|
||||
? T
|
||||
: T extends object
|
||||
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
|
||||
: T
|
||||
|
||||
/**
|
||||
* Make all properties deeply optional.
|
||||
*/
|
||||
export type DeepPartial<T> = T extends object
|
||||
? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||
: T
|
||||
|
||||
/**
|
||||
* Make all properties deeply required.
|
||||
*/
|
||||
export type DeepRequired<T> = T extends object
|
||||
? { [K in keyof T]-?: DeepRequired<T[K]> }
|
||||
: T
|
||||
|
||||
/**
|
||||
* Make all properties deeply mutable (remove readonly).
|
||||
*/
|
||||
export type DeepMutable<T> = T extends object
|
||||
? { -readonly [K in keyof T]: DeepMutable<T[K]> }
|
||||
: T
|
||||
|
||||
// =============================================================================
|
||||
// OBJECT UTILITIES
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Get keys of object where value matches type.
|
||||
*/
|
||||
export type KeysOfType<T, V> = {
|
||||
[K in keyof T]: T[K] extends V ? K : never
|
||||
}[keyof T]
|
||||
|
||||
/**
|
||||
* Pick properties by value type.
|
||||
*/
|
||||
export type PickByType<T, V> = Pick<T, KeysOfType<T, V>>
|
||||
|
||||
/**
|
||||
* Omit properties by value type.
|
||||
*/
|
||||
export type OmitByType<T, V> = Omit<T, KeysOfType<T, V>>
|
||||
|
||||
/**
|
||||
* Make specific keys optional.
|
||||
*/
|
||||
export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
|
||||
|
||||
/**
|
||||
* Make specific keys required.
|
||||
*/
|
||||
export type RequiredBy<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>
|
||||
|
||||
/**
|
||||
* Make specific keys readonly.
|
||||
*/
|
||||
export type ReadonlyBy<T, K extends keyof T> = Omit<T, K> & Readonly<Pick<T, K>>
|
||||
|
||||
/**
|
||||
* Merge two types (second overrides first).
|
||||
*/
|
||||
export type Merge<T, U> = Omit<T, keyof U> & U
|
||||
|
||||
// =============================================================================
|
||||
// ARRAY UTILITIES
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Get element type from array.
|
||||
*/
|
||||
export type ElementOf<T> = T extends (infer E)[] ? E : never
|
||||
|
||||
/**
|
||||
* Tuple of specific length.
|
||||
*/
|
||||
export type Tuple<T, N extends number> = N extends N
|
||||
? number extends N
|
||||
? T[]
|
||||
: _TupleOf<T, N, []>
|
||||
: never
|
||||
|
||||
type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N
|
||||
? R
|
||||
: _TupleOf<T, N, [T, ...R]>
|
||||
|
||||
/**
|
||||
* Non-empty array.
|
||||
*/
|
||||
export type NonEmptyArray<T> = [T, ...T[]]
|
||||
|
||||
/**
|
||||
* At least N elements.
|
||||
*/
|
||||
export type AtLeast<T, N extends number> = [...Tuple<T, N>, ...T[]]
|
||||
|
||||
// =============================================================================
|
||||
// FUNCTION UTILITIES
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Get function arguments as tuple.
|
||||
*/
|
||||
export type Arguments<T> = T extends (...args: infer A) => any ? A : never
|
||||
|
||||
/**
|
||||
* Get first argument of function.
|
||||
*/
|
||||
export type FirstArgument<T> = T extends (first: infer F, ...args: any[]) => any
|
||||
? F
|
||||
: never
|
||||
|
||||
/**
|
||||
* Async version of function.
|
||||
*/
|
||||
export type AsyncFunction<T extends (...args: any[]) => any> = (
|
||||
...args: Parameters<T>
|
||||
) => Promise<Awaited<ReturnType<T>>>
|
||||
|
||||
/**
|
||||
* Promisify return type.
|
||||
*/
|
||||
export type Promisify<T> = T extends (...args: infer A) => infer R
|
||||
? (...args: A) => Promise<Awaited<R>>
|
||||
: never
|
||||
|
||||
// =============================================================================
|
||||
// STRING UTILITIES
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Split string by delimiter.
|
||||
*/
|
||||
export type Split<S extends string, D extends string> =
|
||||
S extends `${infer T}${D}${infer U}`
|
||||
? [T, ...Split<U, D>]
|
||||
: [S]
|
||||
|
||||
/**
|
||||
* Join tuple to string.
|
||||
*/
|
||||
export type Join<T extends string[], D extends string> =
|
||||
T extends []
|
||||
? ''
|
||||
: T extends [infer F extends string]
|
||||
? F
|
||||
: T extends [infer F extends string, ...infer R extends string[]]
|
||||
? `${F}${D}${Join<R, D>}`
|
||||
: never
|
||||
|
||||
/**
|
||||
* Path to nested object.
|
||||
*/
|
||||
export type PathOf<T, K extends keyof T = keyof T> = K extends string
|
||||
? T[K] extends object
|
||||
? K | `${K}.${PathOf<T[K]>}`
|
||||
: K
|
||||
: never
|
||||
|
||||
// =============================================================================
|
||||
// UNION UTILITIES
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Last element of union.
|
||||
*/
|
||||
export type UnionLast<T> = UnionToIntersection<
|
||||
T extends any ? () => T : never
|
||||
> extends () => infer R
|
||||
? R
|
||||
: never
|
||||
|
||||
/**
|
||||
* Union to intersection.
|
||||
*/
|
||||
export type UnionToIntersection<U> = (
|
||||
U extends any ? (k: U) => void : never
|
||||
) extends (k: infer I) => void
|
||||
? I
|
||||
: never
|
||||
|
||||
/**
|
||||
* Union to tuple.
|
||||
*/
|
||||
export type UnionToTuple<T, L = UnionLast<T>> = [T] extends [never]
|
||||
? []
|
||||
: [...UnionToTuple<Exclude<T, L>>, L]
|
||||
|
||||
// =============================================================================
|
||||
// VALIDATION UTILITIES
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Assert type at compile time.
|
||||
*/
|
||||
export type AssertEqual<T, U> =
|
||||
(<V>() => V extends T ? 1 : 2) extends (<V>() => V extends U ? 1 : 2)
|
||||
? true
|
||||
: false
|
||||
|
||||
/**
|
||||
* Ensure type is not never.
|
||||
*/
|
||||
export type IsNever<T> = [T] extends [never] ? true : false
|
||||
|
||||
/**
|
||||
* Ensure type is any.
|
||||
*/
|
||||
export type IsAny<T> = 0 extends 1 & T ? true : false
|
||||
|
||||
/**
|
||||
* Ensure type is unknown.
|
||||
*/
|
||||
export type IsUnknown<T> = IsAny<T> extends true
|
||||
? false
|
||||
: unknown extends T
|
||||
? true
|
||||
: false
|
||||
|
||||
// =============================================================================
|
||||
// JSON UTILITIES
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* JSON-safe types.
|
||||
*/
|
||||
export type JsonPrimitive = string | number | boolean | null
|
||||
export type JsonArray = JsonValue[]
|
||||
export type JsonObject = { [key: string]: JsonValue }
|
||||
export type JsonValue = JsonPrimitive | JsonArray | JsonObject
|
||||
|
||||
/**
|
||||
* Make type JSON-serializable.
|
||||
*/
|
||||
export type Jsonify<T> = T extends JsonPrimitive
|
||||
? T
|
||||
: T extends undefined | ((...args: any[]) => any) | symbol
|
||||
? never
|
||||
: T extends { toJSON(): infer R }
|
||||
? R
|
||||
: T extends object
|
||||
? { [K in keyof T]: Jsonify<T[K]> }
|
||||
: never
|
||||
|
||||
// =============================================================================
|
||||
// EXHAUSTIVE CHECK
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Ensure all cases are handled in switch/if.
|
||||
*/
|
||||
export function assertNever(value: never, message?: string): never {
|
||||
throw new Error(message ?? `Unexpected value: ${value}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Exhaustive check without throwing.
|
||||
*/
|
||||
export function exhaustiveCheck(_value: never): void {
|
||||
// This function should never be called
|
||||
}
|
||||
203
.agent/skills/typescript-expert/scripts/ts_diagnostic.py
Normal file
203
.agent/skills/typescript-expert/scripts/ts_diagnostic.py
Normal file
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
TypeScript Project Diagnostic Script
|
||||
Analyzes TypeScript projects for configuration, performance, and common issues.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
def run_cmd(cmd: str) -> str:
|
||||
"""Run shell command and return output."""
|
||||
try:
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||
return result.stdout + result.stderr
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
def check_versions():
|
||||
"""Check TypeScript and Node versions."""
|
||||
print("\n📦 Versions:")
|
||||
print("-" * 40)
|
||||
|
||||
ts_version = run_cmd("npx tsc --version 2>/dev/null").strip()
|
||||
node_version = run_cmd("node -v 2>/dev/null").strip()
|
||||
|
||||
print(f" TypeScript: {ts_version or 'Not found'}")
|
||||
print(f" Node.js: {node_version or 'Not found'}")
|
||||
|
||||
def check_tsconfig():
|
||||
"""Analyze tsconfig.json settings."""
|
||||
print("\n⚙️ TSConfig Analysis:")
|
||||
print("-" * 40)
|
||||
|
||||
tsconfig_path = Path("tsconfig.json")
|
||||
if not tsconfig_path.exists():
|
||||
print("⚠️ tsconfig.json not found")
|
||||
return
|
||||
|
||||
try:
|
||||
with open(tsconfig_path) as f:
|
||||
config = json.load(f)
|
||||
|
||||
compiler_opts = config.get("compilerOptions", {})
|
||||
|
||||
# Check strict mode
|
||||
if compiler_opts.get("strict"):
|
||||
print("✅ Strict mode enabled")
|
||||
else:
|
||||
print("⚠️ Strict mode NOT enabled")
|
||||
|
||||
# Check important flags
|
||||
flags = {
|
||||
"noUncheckedIndexedAccess": "Unchecked index access protection",
|
||||
"noImplicitOverride": "Implicit override protection",
|
||||
"skipLibCheck": "Skip lib check (performance)",
|
||||
"incremental": "Incremental compilation"
|
||||
}
|
||||
|
||||
for flag, desc in flags.items():
|
||||
status = "✅" if compiler_opts.get(flag) else "⚪"
|
||||
print(f" {status} {desc}: {compiler_opts.get(flag, 'not set')}")
|
||||
|
||||
# Check module settings
|
||||
print(f"\n Module: {compiler_opts.get('module', 'not set')}")
|
||||
print(f" Module Resolution: {compiler_opts.get('moduleResolution', 'not set')}")
|
||||
print(f" Target: {compiler_opts.get('target', 'not set')}")
|
||||
|
||||
except json.JSONDecodeError:
|
||||
print("❌ Invalid JSON in tsconfig.json")
|
||||
|
||||
def check_tooling():
|
||||
"""Detect TypeScript tooling ecosystem."""
|
||||
print("\n🛠️ Tooling Detection:")
|
||||
print("-" * 40)
|
||||
|
||||
pkg_path = Path("package.json")
|
||||
if not pkg_path.exists():
|
||||
print("⚠️ package.json not found")
|
||||
return
|
||||
|
||||
try:
|
||||
with open(pkg_path) as f:
|
||||
pkg = json.load(f)
|
||||
|
||||
all_deps = {**pkg.get("dependencies", {}), **pkg.get("devDependencies", {})}
|
||||
|
||||
tools = {
|
||||
"biome": "Biome (linter/formatter)",
|
||||
"eslint": "ESLint",
|
||||
"prettier": "Prettier",
|
||||
"vitest": "Vitest (testing)",
|
||||
"jest": "Jest (testing)",
|
||||
"turborepo": "Turborepo (monorepo)",
|
||||
"turbo": "Turbo (monorepo)",
|
||||
"nx": "Nx (monorepo)",
|
||||
"lerna": "Lerna (monorepo)"
|
||||
}
|
||||
|
||||
for tool, desc in tools.items():
|
||||
for dep in all_deps:
|
||||
if tool in dep.lower():
|
||||
print(f" ✅ {desc}")
|
||||
break
|
||||
|
||||
except json.JSONDecodeError:
|
||||
print("❌ Invalid JSON in package.json")
|
||||
|
||||
def check_monorepo():
|
||||
"""Check for monorepo configuration."""
|
||||
print("\n📦 Monorepo Check:")
|
||||
print("-" * 40)
|
||||
|
||||
indicators = [
|
||||
("pnpm-workspace.yaml", "PNPM Workspace"),
|
||||
("lerna.json", "Lerna"),
|
||||
("nx.json", "Nx"),
|
||||
("turbo.json", "Turborepo")
|
||||
]
|
||||
|
||||
found = False
|
||||
for file, name in indicators:
|
||||
if Path(file).exists():
|
||||
print(f" ✅ {name} detected")
|
||||
found = True
|
||||
|
||||
if not found:
|
||||
print(" ⚪ No monorepo configuration detected")
|
||||
|
||||
def check_type_errors():
|
||||
"""Run quick type check."""
|
||||
print("\n🔍 Type Check:")
|
||||
print("-" * 40)
|
||||
|
||||
result = run_cmd("npx tsc --noEmit 2>&1 | head -20")
|
||||
if "error TS" in result:
|
||||
errors = result.count("error TS")
|
||||
print(f" ❌ {errors}+ type errors found")
|
||||
print(result[:500])
|
||||
else:
|
||||
print(" ✅ No type errors")
|
||||
|
||||
def check_any_usage():
|
||||
"""Check for any type usage."""
|
||||
print("\n⚠️ 'any' Type Usage:")
|
||||
print("-" * 40)
|
||||
|
||||
result = run_cmd("grep -r ': any' --include='*.ts' --include='*.tsx' src/ 2>/dev/null | wc -l")
|
||||
count = result.strip()
|
||||
if count and count != "0":
|
||||
print(f" ⚠️ Found {count} occurrences of ': any'")
|
||||
sample = run_cmd("grep -rn ': any' --include='*.ts' --include='*.tsx' src/ 2>/dev/null | head -5")
|
||||
if sample:
|
||||
print(sample)
|
||||
else:
|
||||
print(" ✅ No explicit 'any' types found")
|
||||
|
||||
def check_type_assertions():
|
||||
"""Check for type assertions."""
|
||||
print("\n⚠️ Type Assertions (as):")
|
||||
print("-" * 40)
|
||||
|
||||
result = run_cmd("grep -r ' as ' --include='*.ts' --include='*.tsx' src/ 2>/dev/null | grep -v 'import' | wc -l")
|
||||
count = result.strip()
|
||||
if count and count != "0":
|
||||
print(f" ⚠️ Found {count} type assertions")
|
||||
else:
|
||||
print(" ✅ No type assertions found")
|
||||
|
||||
def check_performance():
|
||||
"""Check type checking performance."""
|
||||
print("\n⏱️ Type Check Performance:")
|
||||
print("-" * 40)
|
||||
|
||||
result = run_cmd("npx tsc --extendedDiagnostics --noEmit 2>&1 | grep -E 'Check time|Files:|Lines:|Nodes:'")
|
||||
if result.strip():
|
||||
for line in result.strip().split('\n'):
|
||||
print(f" {line}")
|
||||
else:
|
||||
print(" ⚠️ Could not measure performance")
|
||||
|
||||
def main():
|
||||
print("=" * 50)
|
||||
print("🔍 TypeScript Project Diagnostic Report")
|
||||
print("=" * 50)
|
||||
|
||||
check_versions()
|
||||
check_tsconfig()
|
||||
check_tooling()
|
||||
check_monorepo()
|
||||
check_any_usage()
|
||||
check_type_assertions()
|
||||
check_type_errors()
|
||||
check_performance()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("✅ Diagnostic Complete")
|
||||
print("=" * 50)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user