.claude/skills/enforcing-typescript-standards/SKILL.md
Enforces the project's core TypeScript standards including explicit typing, import organization, class member ordering, and code safety rules. ALWAYS apply when creating, modifying, or reviewing any TypeScript (.ts/.tsx) file.
npx skillsauth add ldapts/ldapts enforcing-typescript-standardsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Enforces the project's core TypeScript standards including explicit typing, import organization, class member ordering, and code safety rules.
Activate this skill when the user says or implies any of these:
.ts or .tsx filesSpecific triggers:
.ts or .tsx filepublic, private, or protectedimport type for types: import type { Foo } from './foo.js'any and type assertions: Prefer proper typing over any or as casts; use them only when truly necessaryRecord<string, unknown> or specific interfaces for JSON data, never anyNumber(value) over parseInt(value, 10) or parseFloat(value)Pick, Omit, Partial, or other utility typesBefore using as, try these approaches in order:
typeof, instanceof)// Bad
const user = data as User;
// Good
function isUser(data: unknown): data is User {
return typeof data === 'object' && data !== null && 'id' in data;
}
if (isUser(data)) {
// data is now typed as User
}
?? over || for defaults (avoids false positives on 0 or '')?. for safe property access_, i, j, k, e, x, y)function foo() not const foo = function()const unless reassignment is neededconst or let{ foo } not { foo: foo }`Hello ${name}` not 'Hello ' + name=== except for null comparisonsreduce: Prefer for...of loops or other array methods for clarityPascalCase (e.g., MyValue)_0/'' (valid values) from null/undefined is semantically importantError when meaningfulAvoid these antipatterns:
console.log() statements in production codeeval() or Function() constructorawait inside loops when Promise.all would be simpler (sequential awaits are fine when order matters or parallelism adds complexity)@ts-ignore without explanation (use @ts-expect-error with 10+ char description)// increment counter above counter++// returns a string when return type is : stringarr.length > 0, str !== '', obj !== null && obj !== undefinedany type or as type assertions|| for defaults when ?? is more appropriatePick/Omit/Partialnpm run build or npx tsc --noEmit)npm run lint to confirm compliance before completing the task// Standard
// Retry with exponential backoff to handle transient network failures
async function fetchWithRetry(url: string, attempts = 3): Promise<Response> {
for (let i = 0; i < attempts; i++) {
try {
return await fetch(url);
} catch {
await sleep(2 ** i * 100);
}
}
throw new Error(`Failed after ${attempts} attempts`);
}
// Non-Standard
/**
* Fetches data from a URL with retry logic
* @param url - The URL to fetch from
* @param attempts - Number of attempts (default 3)
* @returns A Promise that resolves to a Response
*/
async function fetchWithRetry(url: string, attempts = 3): Promise<Response> {
// Loop through attempts
for (let i = 0; i < attempts; i++) {
try {
// Try to fetch the URL
return await fetch(url);
} catch {
// Wait before retrying
await sleep(2 ** i * 100);
}
}
// Throw error if all attempts fail
throw new Error(`Failed after ${attempts} attempts`);
}
// Standard
if (myArray.length) {
}
if (myString) {
}
if (myObject) {
}
if (!value) {
}
// Non-Standard
if (myArray.length !== 0) {
}
if (myArray.length > 0) {
}
if (myString !== '') {
}
if (myObject !== null && myObject !== undefined) {
}
if (value === null || value === undefined) {
}
// Standard
function processUser(user: User | null): Result {
if (!user) {
return { error: 'No user provided' };
}
if (!user.isActive) {
return { error: 'User is inactive' };
}
return { data: transform(user) };
}
// Non-Standard
function processUser(user: User | null): Result {
if (user) {
if (user.isActive) {
return { data: transform(user) };
} else {
return { error: 'User is inactive' };
}
} else {
return { error: 'No user provided' };
}
}
// Standard
export function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
export function formatCurrency(amount: number): string {
return `$${amount.toFixed(2)}`;
}
// Non-Standard
export class Calculator {
static calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
static formatCurrency(amount: number): string {
return `$${amount.toFixed(2)}`;
}
}
// Standard
function transformItem(item: Item): TransformedItem {
return { id: item.id, name: item.name.toUpperCase() };
}
async function processItems(items: Item[]): Promise<TransformedItem[]> {
return items.map(transformItem);
}
// Non-Standard
async function processItems(items: Item[]): Promise<TransformedItem[]> {
function transformItem(item: Item): TransformedItem {
return { id: item.id, name: item.name.toUpperCase() };
}
return items.map(transformItem);
}
// Standard
function addItem(items: Item[], newItem: Item): Item[] {
return [...items, newItem];
}
function removeItem(items: Item[], id: string): Item[] {
return items.filter((item) => item.id !== id);
}
function updateItem(items: Item[], id: string, updates: Partial<Item>): Item[] {
return items.map((item) => (item.id === id ? { ...item, ...updates } : item));
}
// Non-Standard
function addItem(items: Item[], newItem: Item): Item[] {
items.push(newItem);
return items;
}
function removeItem(items: Item[], id: string): Item[] {
const index = items.findIndex((item) => item.id === id);
items.splice(index, 1);
return items;
}
// Standard
async function getUser(id: string): Promise<User> {
return userService.findById(id);
}
// Non-Standard
async function getUser(id: string): Promise<User> {
try {
return await userService.findById(id);
} catch (error) {
console.error(error);
throw error;
}
}
// Standard
async function readConfig(path: string): Promise<Config> {
try {
const content = await readFile(path, 'utf-8');
return JSON.parse(content);
} catch (error) {
if (isNotFoundError(error)) {
return defaultConfig;
}
throw error;
}
}
// Non-Standard
async function readConfig(path: string): Promise<Config> {
if (await fileExists(path)) {
const content = await readFile(path, 'utf-8');
return JSON.parse(content);
}
return defaultConfig;
}
// Given an existing type
interface User {
id: string;
email: string;
name: string;
passwordHash: string;
createdAt: Date;
updatedAt: Date;
}
// Standard - derive from existing type
type PublicUser = Omit<User, 'passwordHash'>;
type UserSummary = Pick<User, 'id' | 'name'>;
type UserUpdate = Partial<Pick<User, 'email' | 'name'>>;
// Non-Standard - duplicating fields that already exist
interface PublicUser {
id: string;
email: string;
name: string;
createdAt: Date;
updatedAt: Date;
}
interface UserSummary {
id: string;
name: string;
}
interface UserUpdate {
email?: string;
name?: string;
}
development
Maintainer-only workflow for handling GitHub Secret Scanning alerts on OpenClaw. Use when Codex needs to triage, redact, clean up, and resolve secret leakage found in issue comments, issue bodies, PR comments, or other GitHub content.
development
Maintainer workflow for OpenClaw releases, prereleases, changelog release notes, and publish validation. Use when Codex needs to prepare or verify stable or beta release steps, align version naming, assemble release notes, check release auth requirements, or validate publish-time commands and artifacts.
development
Run, watch, debug, and extend OpenClaw QA testing with qa-lab and qa-channel. Use when Codex needs to execute the repo-backed QA suite, inspect live QA artifacts, debug failing scenarios, add new QA scenarios, or explain the OpenClaw QA workflow. Prefer the live OpenAI lane with regular openai/gpt-5.4 in fast mode; do not use gpt-5.4-pro or gpt-5.4-mini unless the user explicitly overrides that policy.
development
End-to-end Parallels smoke, upgrade, and rerun workflow for OpenClaw across macOS, Windows, and Linux guests. Use when Codex needs to run, rerun, debug, or interpret VM-based install, onboarding, gateway smoke tests, latest-release-to-main upgrade checks, fresh snapshot retests, or optional Discord roundtrip verification under Parallels.