← スキル一覧に戻る
1. Avoid

typescript
by knoopx
pi.ai config
⭐ 2🍴 0📅 2026年1月21日
SKILL.md
name: typescript description: Always use when working with Typescript projects.
TypeScript Skill
This skill provides templates and workflows for modern TypeScript development focused on type safety, code organization, and maintainability.
Core Principles
- Maximum Strictness: Enable all strict compiler flags to catch errors early
- Type Safety: Avoid
any. Useunknownor branded types where appropriate - Modern ESM: Target
ESNextand use ESM modules exclusively - Self-Documenting Code: Types serve as inline documentation
- Fail Fast: Validate inputs at boundaries, catch errors at compile time
TypeScript Configuration
tsconfig.json - Strict Mode
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"lib": ["ESNext"],
"moduleResolution": "bundler",
"jsx": "react-jsx",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noPropertyAccessFromIndexSignature": true,
"allowUnusedLabels": false,
"allowUnreachableCode": false,
"skipLibCheck": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
tsconfig.json for Bun
If using Bun runtime, include bun-types:
{
"compilerOptions": {
"types": ["bun-types"],
"lib": ["ESNext"],
"target": "ESNext"
}
}
Project Structure
my-project/
├── src/
│ ├── index.ts # Main entry point
│ ├── types/
│ │ ├── index.ts # Type exports
│ │ └── domain.ts # Domain types
│ ├── utils/
│ │ ├── index.ts
│ │ ├── validators.ts
│ │ └── formatters.ts
│ └── services/
│ ├── index.ts
│ └── api.ts
├── tests/
│ ├── unit/
│ │ ├── utils.test.ts
│ │ └── validators.test.ts
│ ├── integration/
│ │ └── api.test.ts
│ └── fixtures/
│ └── test-data.ts
├── package.json
├── tsconfig.json
├── vitest.config.ts # or bun test config
└── README.md
Type Definitions
Strict Typing for Functions
/**
* Add two numbers together.
* @param a First number
* @param b Second number
* @returns The sum
*/
function add(a: number, b: number): number {
return a + b;
}
// Function with optional parameter
function greet(name: string, title?: string): string {
return title ? `${title} ${name}` : `Hello, ${name}`;
}
// Function with rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((acc, n) => acc + n, 0);
}
// Function with object parameter
interface User {
id: number;
name: string;
email: string;
}
function createUser(data: User): void {
console.log(`User ${data.name} created`);
}
Strict Typing for Objects
// Interface for strict object shape
interface Config {
readonly apiUrl: string;
readonly timeout: number;
readonly retries?: number;
}
// Using const assertion for literal types
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3,
} as const;
// Readonly properties
interface ImmutableUser {
readonly id: number;
readonly name: string;
readonly email: string;
}
// Index signatures with constraints
interface StringMap {
[key: string]: string | number;
}
// Mapped types for transformations
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Getters<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
Union and Literal Types
// Discriminated unions for type safety
type ApiResponse<T> =
| { status: "success"; data: T }
| { status: "error"; error: string };
// Literal types for strict values
type Environment = "development" | "staging" | "production";
type LogLevel = "debug" | "info" | "warn" | "error";
// Type guards
function isSuccess<T>(
response: ApiResponse<T>,
): response is { status: "success"; data: T } {
return response.status === "success";
}
// Usage
const response: ApiResponse<User> = { status: "success", data: user };
if (isSuccess(response)) {
console.log(response.data);
}
Generics
// Generic function with constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// Generic class
class Container<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
setValue(value: T): void {
this.value = value;
}
}
// Generic with default type
interface Result<T = void> {
ok: boolean;
value?: T;
error?: Error;
}
// Generic with multiple constraints
function merge<T extends object, U extends object>(a: T, b: U): T & U {
return { ...a, ...b };
}
Branded Types for Runtime Safety
// Create distinct types even if underlying is primitive
type UserId = string & { readonly __brand: "UserId" };
type Email = string & { readonly __brand: "Email" };
// Helper functions to create branded types
function createUserId(id: string): UserId {
if (!id) throw new Error("Invalid user ID");
return id as UserId;
}
function createEmail(email: string): Email {
if (!email.includes("@")) throw new Error("Invalid email");
return email as Email;
}
// Usage - compiler prevents mixing up types
function getUserById(id: UserId): User | null {
// Can't accidentally pass Email here
}
const userId = createUserId("user-123");
const email = createEmail("user@example.com");
getUserById(userId); // ✅ OK
getUserById(email); // ❌ Type error
Utility Types
// Partial - make all properties optional
type PartialUser = Partial<User>;
// Required - make all properties required
type RequiredUser = Required<User>;
// Readonly - make all properties readonly
type ReadonlyUser = Readonly<User>;
// Pick - select specific properties
type UserPreview = Pick<User, "id" | "name">;
// Omit - exclude specific properties
type UserWithoutId = Omit<User, "id">;
// Record - map properties to type
type StatusMap = Record<"pending" | "done" | "failed", number>;
// Exclude/Extract - filter union types
type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;
// ReturnType - extract function return type
type AddReturn = ReturnType<typeof add>;
// InstanceType - extract class instance type
class MyClass {}
type MyInstance = InstanceType<typeof MyClass>;
Common Patterns
Error Handling with Types
// Custom error types
class ValidationError extends Error {
constructor(
public field: string,
message: string,
) {
super(message);
this.name = "ValidationError";
}
}
// Result type for explicit error handling
type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
// Helper function
function ok<T, E>(value: T): Result<T, E> {
return { ok: true, value };
}
function err<T, E>(error: E): Result<T, E> {
return { ok: false, error };
}
// Usage
function parseJson(json: string): Result<unknown, SyntaxError> {
try {
return ok(JSON.parse(json));
} catch (e) {
return err(e as SyntaxError);
}
}
const result = parseJson(someString);
if (result.ok) {
console.log(result.value);
} else {
console.error(result.error);
}
Module Augmentation
// Extend types from third-party libraries
declare module "some-library" {
interface SomeType {
customProperty: string;
}
}
// Extend global types
declare global {
interface Window {
myCustomApi: {
doSomething(): Promise<void>;
};
}
}
Conditional Types
// Type that depends on another type
type IsString<T> = T extends string ? true : false;
type A = IsString<"hello">; // true
type B = IsString<number>; // false
// More practical example
type Flatten<T> = T extends Array<infer U> ? U : T;
type Str = Flatten<string[]>; // string
type Num = Flatten<number>; // number
// Extract array element type
type ElementOf<T> = T extends (infer E)[] ? E : never;
Best Practices
1. Avoid any
// ❌ Bad - lose all type information
function processData(data: any): any {
return data.someProperty;
}
// ✅ Good - use unknown for uncertain types
function processData(data: unknown): void {
if (typeof data === "string") {
console.log(data.toUpperCase());
}
}
// ✅ Better - use generics
function processData<T>(data: T): T {
return data;
}
2. Make Invalid States Unrepresentable
// ❌ Bad - allows invalid combinations
interface PageState {
isLoading: boolean;
data?: User;
error?: string;
}
// ✅ Good - types prevent invalid states
type PageState =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: User }
| { status: "error"; error: string };
3. Type at Boundaries
// Define types at API boundaries
interface ApiUser {
id: string;
name: string;
email: string;
}
// Convert to domain types
interface DomainUser {
id: UserId;
name: string;
email: Email;
}
function convertApiUser(apiUser: ApiUser): DomainUser {
return {
id: createUserId(apiUser.id),
name: apiUser.name,
email: createEmail(apiUser.email),
};
}
4. Use Constants for String Literals
// ❌ Bad - magic strings scattered
function log(level: string) {
if (level === "debug") {
}
}
// ✅ Good - centralized constants with types
const LogLevels = {
DEBUG: "debug",
INFO: "info",
WARN: "warn",
ERROR: "error",
} as const;
type LogLevel = (typeof LogLevels)[keyof typeof LogLevels];
function log(level: LogLevel) {
// Autocomplete and type checking
}
5. Document with JSDoc
/**
* Validates an email address.
*
* @param email - The email to validate
* @returns true if email is valid, false otherwise
*
* @example
* ```ts
* isValidEmail("user@example.com") // true
* isValidEmail("invalid") // false
* ```
*/
function isValidEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
6. Don't Pointlessly Rescue Exceptions and Silence Them
// ❌ Bad - silently swallowing errors makes debugging impossible
try {
riskyOperation();
} catch (error) {
// Do nothing - error is lost forever
}
// ❌ Bad - logging but not handling properly
try {
riskyOperation();
} catch (error) {
console.log("Something went wrong"); // Not enough context
}
// ✅ Good - handle errors appropriately
try {
riskyOperation();
} catch (error) {
console.error("Failed to perform operation:", error);
throw error; // Re-throw if caller should handle it
}
// ✅ Good - catch specific errors
try {
riskyOperation();
} catch (error) {
if (error instanceof ValidationError) {
// Handle validation error specifically
return { status: "invalid", message: error.message };
} else {
// Re-throw unexpected errors
throw error;
}
}
7. Avoid Creating Temporary Files When You Can Pipe a Stream
// ❌ Bad - creates temporary files unnecessarily, potential race conditions, filesystem overhead
import { writeFileSync, readFileSync, unlinkSync } from "node:fs";
import { execSync } from "node:child_process";
function processData(input: string): string {
const tempFile = "/tmp/temp.txt";
writeFileSync(tempFile, input);
const result = execSync(`process ${tempFile}`, { encoding: "utf8" });
unlinkSync(tempFile);
return result;
}
// ✅ Good - use streams for efficiency, safety, and reduced I/O
import { spawn } from "node:child_process";
async function processData(input: string): Promise<string> {
return new Promise((resolve, reject) => {
const proc = spawn("process", [], { stdio: ["pipe", "pipe", "pipe"] });
let output = "";
proc.stdout.on("data", (data) => (output += data.toString()));
proc.on("close", (code) => {
if (code === 0) resolve(output);
else reject(new Error("Process failed"));
});
proc.on("error", reject);
proc.stdin.write(input);
proc.stdin.end();
});
}
Testing TypeScript Code
Unit Tests with Vitest
import { describe, it, expect } from "vitest";
import { add, greet } from "./math";
describe("math utilities", () => {
it("adds two numbers", () => {
expect(add(2, 3)).toBe(5);
expect(add(-1, 1)).toBe(0);
});
it("greets with optional title", () => {
expect(greet("Alice")).toBe("Hello, Alice");
expect(greet("Bob", "Mr")).toBe("Mr Bob");
});
});
Type Testing
import { expectType, expectAssignable } from "vitest";
// Verify function has correct type
expectType<(a: number, b: number) => number>(add);
// Verify type assignability
interface User {
id: number;
name: string;
}
const user = { id: 1, name: "Alice", extra: "prop" };
expectAssignable<User>(user);
Migrating to TypeScript
Gradual Adoption
- Enable TypeScript compilation without strict checks
- Gradually enable strict flags one by one
- Fix type errors systematically
- Use
allowJsto mix JS and TS during transition
Configuration for Gradual Migration
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"strict": false,
"noImplicitAny": false
}
}
Common Gotchas
Type Narrowing
// ❌ Doesn't narrow properly
if (typeof x === "string" || typeof x === "number") {
// Can still be either type here
}
// ✅ Proper narrowing
if (typeof x === "string") {
// x is string
} else if (typeof x === "number") {
// x is number
}
Object Mutability
// Be explicit about mutability
interface MutableConfig {
timeout: number;
retries: number;
}
interface ImmutableConfig {
readonly timeout: number;
readonly retries: number;
}
This Binding
// ❌ Can lose `this` context
const handler = user.handleClick;
document.addEventListener("click", handler);
// ✅ Preserve `this` with arrow function
document.addEventListener("click", () => user.handleClick());
// ✅ Or bind explicitly
document.addEventListener("click", user.handleClick.bind(user));
Performance Considerations
- Keep Types Simple: Complex type operations can slow compilation
- Use
noEmit: When only doing type checking, skip emitting JavaScript - Incremental Mode: Use
incremental: truefor faster rebuilds - Type Checking: In CI, use
tsc --noEmitfor fast type checking - Declaration Files: Build declaration files once, reuse across monorepo
Related Skills
- bun: Use Bun as a fast runtime for executing TypeScript code without compilation, or manage TypeScript projects with Bun's tooling.
- vitest: Write and run tests for TypeScript code with Vitest, ensuring type safety in your test suites.
- knip: Identify and remove unused dependencies, files, and exports in TypeScript projects.
- ast-grep: Find complex code patterns and perform safe structural transformations in TypeScript codebases.
- jscpd: Detect and analyze duplicate code in TypeScript projects.
Related Tools
- lsp: Query language server for definitions, references, types, symbols, diagnostics, rename, and code actions.
- generate-codemap: Generate a compact map of the codebase structure, symbols, and dependencies.
- analyze-dependencies: Analyze dependency tree for files or show external packages used in the project.
スコア
総合スコア
50/100
リポジトリの品質指標に基づく評価
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
レビュー
💬
レビュー機能は近日公開予定です