惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
B
Blog
M
MIT News - Artificial intelligence
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
C
Check Point Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
J
Java Code Geeks
博客园 - 叶小钗
S
SegmentFault 最新的问题
Last Week in AI
Last Week in AI
T
The Blog of Author Tim Ferriss
Microsoft Security Blog
Microsoft Security Blog
小众软件
小众软件
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 【当耐特】
腾讯CDC
博客园 - Franky
博客园 - 聂微东
V
Visual Studio Blog
GbyAI
GbyAI
Martin Fowler
Martin Fowler
罗磊的独立博客
Y
Y Combinator Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
TypeScript Best Practices for Next.js Projects in 2026
TheKitBase · 2026-06-14 · via DEV Community
Cover image for TypeScript Best Practices for Next.js Projects in 2026

TheKitBase

Most Next.js projects say TypeScript and don't mean it. The tsconfig.json has strict: true but the code is full of type assertions, implicit any fallbacks, and unguarded array index accesses that crash at runtime. Proper TypeScript is a configuration choice first - then a set of patterns.

1. The tsconfig.json that actually matters

strict: true enables eight compiler options but misses several that eliminate entire categories of runtime errors:

{
  "compilerOptions": {
    "strict": true,

    "noUncheckedIndexedAccess": true,
    // arr[0] is T | undefined, not T.
    // Catches "cannot read property of undefined" at compile time.

    "noImplicitOverride": true,
    // Subclass methods that override parent must use the override keyword.

    "exactOptionalPropertyTypes": true,
    // { x?: string } is not the same as { x: string | undefined }.

    "forceConsistentCasingInFileNames": true,
    // Prevents import path casing bugs that only show on Linux CI.

    "noPropertyAccessFromIndexSignature": true
    // obj.unknownKey must be obj["unknownKey"] for indexed types.
  }
}

2. noUncheckedIndexedAccess - the flag most teams skip

The most impactful flag not included in strict. Without it, array[0] is typed as T even when the array might be empty:

// Without noUncheckedIndexedAccess:
const items: string[] = [];
const first = items[0];   // TypeScript says: string
first.toUpperCase();       // Runtime crash

// With noUncheckedIndexedAccess:
const first = items[0];   // TypeScript says: string | undefined ✓
first?.toUpperCase();      // Must handle undefined

Same for object index signatures:

const map: Record<string, number> = {};
const val = map["key"]; // number | undefined (with the flag)
if (val !== undefined) {
  console.log(val * 2); // number - safe
}

3. Type-safe Server Actions

Server Actions receive FormData which is untyped. Parse and validate at the top of every action:

// ❌ Type assertion bypasses safety
async function createPost(formData: FormData) {
  "use server";
  const title = formData.get("title") as string;
  await db.posts.create({ title });
}

// ✅ Validated with zod - unknown input becomes typed at runtime
import { z } from "zod";

const PostSchema = z.object({
  title: z.string().min(1).max(200),
  content: z.string().min(1),
});

async function createPost(formData: FormData) {
  "use server";
  const result = PostSchema.safeParse({
    title: formData.get("title"),
    content: formData.get("content"),
  });
  if (!result.success) {
    return { error: result.error.flatten().fieldErrors };
  }
  await db.posts.create(result.data); // result.data is fully typed
}

4. Type-safe fetch

response.json() returns Promise<any>. Use a typed wrapper:

async function fetchTyped<T>(
  url: string,
  schema: z.ZodType<T>,
  options?: RequestInit
): Promise<T> {
  const res = await fetch(url, options);
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${url}`);
  return schema.parse(await res.json()); // throws if shape doesn't match
}

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
});

const user = await fetchTyped("/api/user/123", UserSchema);
// user: { id: string; name: string; email: string }

5. Replace type assertions with type guards

Type assertions (value as SomeType) tell the type checker to stop checking:

// ❌ Type assertion - crash becomes a runtime problem
const user = data as User;

// ✅ Type guard - check at runtime
function isUser(data: unknown): data is User {
  return (
    typeof data === "object" &&
    data !== null &&
    "id" in data &&
    "email" in data
  );
}

// ❌ Asserting env vars exist
const apiKey = process.env.API_KEY as string;

// ✅ Validate at startup
function requireEnv(key: string): string {
  const value = process.env[key];
  if (!value) throw new Error(`Missing env var: ${key}`);
  return value;
}
const apiKey = requireEnv("API_KEY");

6. Discriminated unions over optional props

Optional props that only make sense together allow invalid state. Discriminated unions make it unrepresentable:

// ❌ Optional props allow invalid combinations
interface ButtonProps {
  variant?: "default" | "loading" | "error";
  loadingText?: string;
  errorMessage?: string;
}

// ✅ Discriminated union - invalid state is unrepresentable
type ButtonProps =
  | { variant: "default"; label: string }
  | { variant: "loading"; loadingText: string }
  | { variant: "error"; errorMessage: string; onRetry: () => void };

function Button(props: ButtonProps) {
  if (props.variant === "loading") {
    return <Spinner text={props.loadingText} />; // loadingText is string, not string | undefined
  }
  // ...
}

Common mistakes and their fixes

Mistake Correct pattern
response.json() returns any Typed fetch wrapper with zod
formData.get() as string Parse FormData with zod in Server Actions
arr[0] treated as T Enable noUncheckedIndexedAccess
value as SomeType everywhere Write type guards or use zod.parse()
Optional props for related data Discriminated unions
process.env.KEY as string requireEnv() utility
any in third-party types Typed adapter at the integration boundary

Originally published at thekitbase.app