typescript
TypeScript 5.x patterns and type definitions for this project
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
TypeScript 5.x patterns and type definitions for this project
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | typescript |
| description | TypeScript 5.x patterns and type definitions for this project |
This project uses TypeScript 5.x in strict mode with Next.js and React. TypeScript provides static type checking, better IDE support, and helps catch errors before runtime.
Detected Version: TypeScript 5.x
Strict Mode: Enabled (strict: true)
Target: ES2017
Module: ESNext with bundler resolution
JSX: react-jsx (React 19)
Configuration (tsconfig.json):
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"paths": {
"@/*": ["./src/*"]
}
}
}
// src/lib/types.ts
export interface Task {
id: string;
description: string;
completed: boolean;
createdAt: Date;
}
export type TaskFilter = 'all' | 'active' | 'completed';
// Or use enum for better IDE support
export enum TaskFilter {
All = 'all',
Active = 'active',
Completed = 'completed',
}
export interface TaskStats {
total: number;
active: number;
completed: number;
}
// TaskInput component props
export interface TaskInputProps {
onAddTask: (description: string) => void;
}
// TaskItem component props
export interface TaskItemProps {
task: Task;
onToggle: (id: string) => void;
onDelete: (id: string) => void;
}
// TaskList component props
export interface TaskListProps {
tasks: Task[];
onToggle: (id: string) => void;
onDelete: (id: string) => void;
}
// TaskFilters component props
export interface TaskFiltersProps {
currentFilter: TaskFilter;
onFilterChange: (filter: TaskFilter) => void;
stats: TaskStats;
}
import { FC } from 'react';
// Explicit props interface (recommended)
interface TaskItemProps {
task: Task;
onToggle: (id: string) => void;
}
// Option 1: Type inference (recommended)
export default function TaskItem({ task, onToggle }: TaskItemProps) {
return (/* JSX */);
}
// Option 2: FC type (less common in 2026)
const TaskItem: FC<TaskItemProps> = ({ task, onToggle }) => {
return (/* JSX */);
};
import { useState } from 'react';
// Type inference (simple types)
const [count, setCount] = useState(0); // number inferred
// Explicit type (complex types)
const [tasks, setTasks] = useState<Task[]>([]);
const [filter, setFilter] = useState<TaskFilter>('all');
// Optional/nullable state
const [error, setError] = useState<string | null>(null);
const [selectedTask, setSelectedTask] = useState<Task | undefined>(undefined);
import { useRef } from 'react';
// DOM element refs
const inputRef = useRef<HTMLInputElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
// Usage
inputRef.current?.focus();
inputRef.current?.value; // typed as string | undefined
import { FormEvent, ChangeEvent, MouseEvent, KeyboardEvent } from 'react';
// Form submit
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
};
// Input change
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const value = e.target.value; // typed as string
};
// Button click
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
};
// Keyboard events
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
// Handle enter
}
};
// hooks/useTasks.ts
import { useState } from 'react';
import { Task } from '@/lib/types';
interface UseTasksReturn {
tasks: Task[];
addTask: (description: string) => void;
toggleTask: (id: string) => void;
deleteTask: (id: string) => void;
}
export function useTasks(): UseTasksReturn {
const [tasks, setTasks] = useState<Task[]>([]);
const addTask = (description: string) => {
const newTask: Task = {
id: crypto.randomUUID(),
description,
completed: false,
createdAt: new Date(),
};
setTasks([...tasks, newTask]);
};
const toggleTask = (id: string) => {
setTasks(tasks.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
));
};
const deleteTask = (id: string) => {
setTasks(tasks.filter(task => task.id !== id));
};
return { tasks, addTask, toggleTask, deleteTask };
}
// App Router page props
interface PageProps {
params: Promise<{ id: string }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}
export default async function Page({ params, searchParams }: PageProps) {
const { id } = await params;
const query = await searchParams;
// ...
}
interface LayoutProps {
children: React.ReactNode;
}
export default function Layout({ children }: LayoutProps) {
return <div>{children}</div>;
}
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Task Manager',
description: 'Simple task management app',
};
any Type// ❌ Bad
const handleChange = (e: any) => {
console.log(e.target.value);
};
// ✅ Good
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
// ❌ Over-specified
const count: number = 5;
const name: string = 'Task';
// ✅ Good (type inferred)
const count = 5;
const name = 'Task';
// ✅ Good (type needed)
const tasks: Task[] = [];
const filter: TaskFilter = 'all';
// ✅ Interface for object shapes
interface Task {
id: string;
description: string;
completed: boolean;
}
// ✅ Type for unions and primitives
type TaskFilter = 'all' | 'active' | 'completed';
type Status = 'pending' | 'loading' | 'success' | 'error';
interface Task {
readonly id: string;
description: string;
completed: boolean;
readonly createdAt: Date;
}
// Or use Readonly utility type
type ReadonlyTask = Readonly<Task>;
// Optional chaining
const taskDescription = selectedTask?.description;
inputRef.current?.focus();
// Nullish coalescing
const count = taskCount ?? 0;
const filter = urlFilter ?? 'all';
// Make all properties optional
type PartialTask = Partial<Task>;
const updateTask = (id: string, updates: Partial<Task>) => {
// updates can have any subset of Task properties
};
// Make all properties required
type RequiredTask = Required<Partial<Task>>;
// Pick specific properties
type TaskPreview = Pick<Task, 'id' | 'description'>;
// Omit specific properties
type NewTask = Omit<Task, 'id' | 'createdAt'>;
const createTask = (task: NewTask): Task => ({
...task,
id: crypto.randomUUID(),
createdAt: new Date(),
});
// Object with specific key-value types
type TasksById = Record<string, Task>;
const tasksMap: TasksById = {
'task-1': { id: 'task-1', /* ... */ },
'task-2': { id: 'task-2', /* ... */ },
};
function isTask(value: unknown): value is Task {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'description' in value &&
'completed' in value
);
}
// Usage
if (isTask(data)) {
// data is typed as Task here
console.log(data.description);
}
// Generic function
function getFirstItem<T>(items: T[]): T | undefined {
return items[0];
}
const firstTask = getFirstItem<Task>(tasks);
// Generic component
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <>{items.map(renderItem)}</>;
}
type ApiResponse<T> =
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: string };
function TaskLoader({ response }: { response: ApiResponse<Task[]> }) {
// TypeScript narrows the type based on status
if (response.status === 'loading') {
return <Spinner />;
}
if (response.status === 'error') {
return <Error message={response.error} />;
}
// TypeScript knows response.data exists here
return <TaskList tasks={response.data} />;
}
// String enum
enum TaskPriority {
Low = 'low',
Medium = 'medium',
High = 'high',
Critical = 'critical',
}
// Usage
const priority: TaskPriority = TaskPriority.High;
// Numeric enum
enum TaskStatus {
Pending, // 0
Active, // 1
Completed, // 2
}
Note: Prefer string literal unions over enums for simpler types:
// Simpler alternative to enum
type TaskPriority = 'low' | 'medium' | 'high' | 'critical';
// Use only when TypeScript can't infer the type
const input = document.getElementById('task-input') as HTMLInputElement;
// Non-null assertion (use sparingly)
const inputValue = inputRef.current!.value;
// Better: Optional chaining
const inputValue = inputRef.current?.value;
// Extend existing types (if needed)
declare module 'next' {
interface PageProps {
customProp?: string;
}
}
any// ❌ Bad
function handleData(data: any) {
console.log(data.value);
}
// ✅ Good
function handleData(data: Task) {
console.log(data.description);
}
// ✅ Better: Use unknown for truly unknown data
function handleData(data: unknown) {
if (isTask(data)) {
console.log(data.description);
}
}
// ❌ Bad
const task = data as Task; // No validation
// ✅ Good
const task = isTask(data) ? data : null;
// ❌ Bad (with strictNullChecks off)
function getDescription(task: Task | null) {
return task.description; // Runtime error if task is null
}
// ✅ Good
function getDescription(task: Task | null): string | null {
return task?.description ?? null;
}
Already configured in tsconfig.json:
// Use @/ instead of relative paths
import { Task } from '@/lib/types';
import TaskInput from '@/components/TaskInput';
// Instead of
import { Task } from '../../lib/types';
import TaskInput from '../components/TaskInput';
# Type check without emitting files
npx tsc --noEmit
# Watch mode for type checking
npx tsc --noEmit --watch