| name | typescript |
| description | TypeScript 5.x patterns and type definitions for this project |
TypeScript 5.x Skill
Overview
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.
Project Configuration
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/*"]
}
}
}
Core Types for Task Manager
Task Interface
export interface Task {
id: string;
description: string;
completed: boolean;
createdAt: Date;
}
Filter Types
export type TaskFilter = 'all' | 'active' | 'completed';
export enum TaskFilter {
All = 'all',
Active = 'active',
Completed = 'completed',
}
Task Statistics
export interface TaskStats {
total: number;
active: number;
completed: number;
}
Component Props Interfaces
export interface TaskInputProps {
onAddTask: (description: string) => void;
}
export interface TaskItemProps {
task: Task;
onToggle: (id: string) => void;
onDelete: (id: string) => void;
}
export interface TaskListProps {
tasks: Task[];
onToggle: (id: string) => void;
onDelete: (id: string) => void;
}
export interface TaskFiltersProps {
currentFilter: TaskFilter;
onFilterChange: (filter: TaskFilter) => void;
stats: TaskStats;
}
React & TypeScript Patterns
Function Components
import { FC } from 'react';
interface TaskItemProps {
task: Task;
onToggle: (id: string) => void;
}
export default function TaskItem({ task, onToggle }: TaskItemProps) {
return ();
}
const TaskItem: FC<TaskItemProps> = ({ task, onToggle }) => {
return ();
};
useState with TypeScript
import { useState } from 'react';
const [count, setCount] = useState(0);
const [tasks, setTasks] = useState<Task[]>([]);
const [filter, setFilter] = useState<TaskFilter>('all');
const [error, setError] = useState<string | null>(null);
const [selectedTask, setSelectedTask] = useState<Task | undefined>(undefined);
useRef with TypeScript
import { useRef } from 'react';
const inputRef = useRef<HTMLInputElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
inputRef.current?.focus();
inputRef.current?.value;
Event Handlers
import { FormEvent, ChangeEvent, MouseEvent, KeyboardEvent } from 'react';
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
};
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
};
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
};
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
}
};
Custom Hooks
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 };
}
Next.js Specific Types
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;
}
Layout Props
interface LayoutProps {
children: React.ReactNode;
}
export default function Layout({ children }: LayoutProps) {
return <div>{children}</div>;
}
Metadata
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Task Manager',
description: 'Simple task management app',
};
TypeScript Best Practices (2026)
1. Avoid any Type
const handleChange = (e: any) => {
console.log(e.target.value);
};
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
2. Use Type Inference When Obvious
const count: number = 5;
const name: string = 'Task';
const count = 5;
const name = 'Task';
const tasks: Task[] = [];
const filter: TaskFilter = 'all';
3. Use Interfaces for Objects, Types for Unions
interface Task {
id: string;
description: string;
completed: boolean;
}
type TaskFilter = 'all' | 'active' | 'completed';
type Status = 'pending' | 'loading' | 'success' | 'error';
4. Use Readonly for Immutable Data
interface Task {
readonly id: string;
description: string;
completed: boolean;
readonly createdAt: Date;
}
type ReadonlyTask = Readonly<Task>;
5. Use Optional Chaining and Nullish Coalescing
const taskDescription = selectedTask?.description;
inputRef.current?.focus();
const count = taskCount ?? 0;
const filter = urlFilter ?? 'all';
Utility Types
Partial
type PartialTask = Partial<Task>;
const updateTask = (id: string, updates: Partial<Task>) => {
};
Required
type RequiredTask = Required<Partial<Task>>;
Pick
type TaskPreview = Pick<Task, 'id' | 'description'>;
Omit
type NewTask = Omit<Task, 'id' | 'createdAt'>;
const createTask = (task: NewTask): Task => ({
...task,
id: crypto.randomUUID(),
createdAt: new Date(),
});
Record
type TasksById = Record<string, Task>;
const tasksMap: TasksById = {
'task-1': { id: 'task-1', },
'task-2': { id: 'task-2', },
};
Type Guards
function isTask(value: unknown): value is Task {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'description' in value &&
'completed' in value
);
}
if (isTask(data)) {
console.log(data.description);
}
Generic Types
function getFirstItem<T>(items: T[]): T | undefined {
return items[0];
}
const firstTask = getFirstItem<Task>(tasks);
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <>{items.map(renderItem)}</>;
}
Discriminated Unions
type ApiResponse<T> =
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: string };
function TaskLoader({ response }: { response: ApiResponse<Task[]> }) {
if (response.status === 'loading') {
return <Spinner />;
}
if (response.status === 'error') {
return <Error message={response.error} />;
}
return <TaskList tasks={response.data} />;
}
Enums
enum TaskPriority {
Low = 'low',
Medium = 'medium',
High = 'high',
Critical = 'critical',
}
const priority: TaskPriority = TaskPriority.High;
enum TaskStatus {
Pending,
Active,
Completed,
}
Note: Prefer string literal unions over enums for simpler types:
type TaskPriority = 'low' | 'medium' | 'high' | 'critical';
Type Assertions
const input = document.getElementById('task-input') as HTMLInputElement;
const inputValue = inputRef.current!.value;
const inputValue = inputRef.current?.value;
Module Augmentation
declare module 'next' {
interface PageProps {
customProp?: string;
}
}
Common Pitfalls
❌ Don't: Use any
function handleData(data: any) {
console.log(data.value);
}
function handleData(data: Task) {
console.log(data.description);
}
function handleData(data: unknown) {
if (isTask(data)) {
console.log(data.description);
}
}
❌ Don't: Type assertion without validation
const task = data as Task;
const task = isTask(data) ? data : null;
❌ Don't: Ignore strict null checks
function getDescription(task: Task | null) {
return task.description;
}
function getDescription(task: Task | null): string | null {
return task?.description ?? null;
}
Path Aliases
Already configured in tsconfig.json:
import { Task } from '@/lib/types';
import TaskInput from '@/components/TaskInput';
import { Task } from '../../lib/types';
import TaskInput from '../components/TaskInput';
Type Checking Commands
npx tsc --noEmit
npx tsc --noEmit --watch
References