Capítulo 265 de 456

TypeScript

Core Idea

Página de referência completa da experiência TypeScript-first do Next.js: setup automático, uso do TypeScript 7, o plugin de IDE, type safety end-to-end no App Router, geração de tipos de rota, next-env.d.ts, tipagem de next.config.ts, links estaticamente tipados, IntelliSense de env vars e como lidar com erros de tipo no build.

Key Concepts

  • Setup automático: create-next-app já configura TypeScript; num projeto existente, renomear um arquivo para .ts/.tsx e rodar next dev/next build instala dependências e gera tsconfig.json automaticamente.
  • TypeScript 7: não expõe a API JS do compiler ainda; Next.js usa o tsc CLI local por default (experimental.useTypeScriptCli), permitindo TS7 funcionar.
  • IDE Plugin: plugin TypeScript custom (ativar via "Use Workspace Version" no VS Code) que valida segment config options, mostra docs em contexto, garante uso correto de 'use client' e que client hooks só sejam usados em Client Components.
  • End-to-end type safety: sem serialização de dados entre fetch e página (pode usar Date, Map, Set direto), e fluxo de dados simplificado com colocated data fetching (sem _app boundary do Pages Router).
  • Route-Aware Type Helpers: PageProps, LayoutProps, RouteContext, globais e gerados sem import, via next dev/next build/next typegen.
  • next-env.d.ts: arquivo gerado (não editar manualmente), referencia tipos do Next.js; deve estar em .gitignore e no array include do tsconfig.json.
  • next.config.ts: tipagem nativa da config; resolução de módulo limitada a CommonJS por default, mas ESM (await, import() dinâmico) disponível via Node.js native TypeScript resolver (Node 22.10.0+, auto-enabled 22.18.0+).
  • Statically Typed Links: requer typedRoutes: true + TypeScript; tipa href de next/link e métodos push/replace/prefetch de next/navigation no App Router (não tipa next/router do Pages Router). Strings literais são validadas automaticamente; não-literais exigem cast as Route.
  • Type IntelliSense para env vars: experimental.typedEnv: true gera .d.ts em .next/types com as env vars carregadas em dev (exclui .env.production* por default, a menos que rode com NODE_ENV=production).
  • typescript.tsconfigPath: aponta para um tsconfig alternativo para build/tooling.
  • typescript.ignoreBuildErrors: pula completamente a checagem de tipos no next build.

Code Examples

import type { NextConfig } from 'next'

// Top-level await and dynamic import are supported
const flags = await import('./flags.js').then((m) => m.default ?? m)

const nextConfig: NextConfig = {
  typedRoutes: Boolean(flags?.typedRoutes),
}

export default nextConfig
  • O que demonstra: usar ESM (top-level await, dynamic import) em next.config.mts num projeto CommonJS, via Node.js native TypeScript resolver.
'use client'

import type { Route } from 'next'
import Link from 'next/link'
import { useRouter } from 'next/navigation'

export default function Example() {
  const router = useRouter()
  const slug = 'nextjs'

  return (
    <>
      <Link href="/about" />
      <Link href={`/blog/${slug}`} />
      <Link href={('/blog/' + slug) as Route} />
      {/* TypeScript error if href is not a valid route */}
      <Link href="/aboot" />

      <button onClick={() => router.push('/about')}>Push About</button>
      <button onClick={() => router.push(('/blog/' + slug) as Route)}>
        Push Non-literal Blog
      </button>
    </>
  )
}
  • O que demonstra: links estaticamente tipados funcionando com strings literais, template strings dinâmicas, e cast as Route para strings não-literais, tanto em Link quanto em useRouter().
function Card<T extends string>({ href }: { href: Route<T> | URL }) {
  return (
    <Link href={href}>
      <div>My Card</div>
    </Link>
  )
}
  • O que demonstra: componente wrapper genérico que aceita href tipado, preservando a checagem de rota do Link.
const nextConfig: NextConfig = {
  typescript: {
    tsconfigPath: isProd ? 'tsconfig.build.json' : 'tsconfig.json',
  },
}
  • O que demonstra: alternar entre tsconfig de produção (mais permissivo, ex. para monorepo) e o tsconfig padrão mais estrito, mantendo o IDE estrito mas o build relaxado.

Reference Tables

VersionChanges
v15.0.0next.config.ts support added for TypeScript projects.
v13.2.0Statically typed links available in beta.
v12.0.0SWC passa a compilar TypeScript/TSX por default (builds mais rápidos).
v10.2.1Suporte a incremental type checking quando habilitado no tsconfig.json.

Anti-patterns

  • Editar next-env.d.ts manualmente: é regenerado automaticamente e suas edições serão sobrescritas; crie um new-types.d.ts separado e referencie-o no tsconfig.json para custom type declarations.
  • Usar TypeScript < 5.1.3 ou @types/react < 18.2.8 com async Server Components: gera erro de tipo 'Promise<Element>' is not a valid JSX element.
  • Configurar projeto sem create-next-app e esquecer .next/types/**/*.ts no tsconfig.json: quebra os links estaticamente tipados.
  • Editar um tsconfig alternativo (via tsconfigPath) em dev esperando hot-reload: só tsconfig.json é observado por mudanças em dev; outro arquivo exige restart do dev server.

Key Takeaways

  1. next.config.ts roda em CommonJS por padrão; ESM completo (top-level await) só via native TypeScript resolver do Node 22.10+, com next.config.mts recomendado para projetos CommonJS.
  2. Statically Typed Links só cobrem next/link e next/navigation no App Router; Pages Router e next/router ficam fora.
  3. typescript.ignoreBuildErrors é diferente de rodar tsc --noEmit manualmente antes do build (a doc recomenda isso como alternativa mais segura em CI/CD).
  4. tsconfigPath separado permite IDE estrito + build de produção relaxado (útil em monorepos com dependências que não seguem os mesmos padrões de tipo).
  5. typedEnv só reflete env vars carregadas em runtime de dev, precisa rodar com NODE_ENV=production para incluir variáveis de produção no IntelliSense.

Connects To

  • typescript (config/next-config-js/typescript): página de referência enxuta das duas opções ignoreBuildErrors/tsconfigPath.
  • useTypeScriptCli: mecanismo (CLI tsc vs API JS) por trás do type-checking do build, citado aqui na seção "Using TypeScript 7".
  • typedRoutes: flag necessária para Statically Typed Links.
  • file-conventions/page, layout, route: fontes dos helpers PageProps, LayoutProps, RouteContext.