Next.js TypeScript集成 #
一、TypeScript配置 #
1.1 tsconfig.json #
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": "preserve",
"incremental": true,
"plugins": [{ "name": "next" }],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
1.2 严格模式 #
json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
二、类型定义 #
2.1 组件Props #
tsx
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'outline'
size?: 'sm' | 'md' | 'lg'
disabled?: boolean
loading?: boolean
children: React.ReactNode
onClick?: () => void
}
export function Button({
variant = 'primary',
size = 'md',
disabled = false,
loading = false,
children,
onClick,
}: ButtonProps) {
return (
<button
className={cn(styles.base, styles[variant], styles[size])}
disabled={disabled || loading}
onClick={onClick}
>
{loading ? <Spinner /> : children}
</button>
)
}
2.2 页面Props #
tsx
interface PageProps {
params: Promise<{ id: string }>
searchParams: Promise<{ page?: string }>
}
export default async function ProductPage({ params, searchParams }: PageProps) {
const { id } = await params
const { page = '1' } = await searchParams
const product = await getProduct(id)
return <ProductDetails product={product} />
}
2.3 API响应类型 #
tsx
interface ApiResponse<T> {
data: T
message: string
success: boolean
}
interface PaginatedResponse<T> extends ApiResponse<T[]> {
pagination: {
page: number
limit: number
total: number
totalPages: number
}
}
export async function getProducts(): Promise<PaginatedResponse<Product>> {
const res = await fetch('/api/products')
return res.json()
}
2.4 环境变量类型 #
typescript
namespace NodeJS {
interface ProcessEnv {
NEXT_PUBLIC_API_URL: string
DATABASE_URL: string
SECRET_KEY: string
NEXT_PUBLIC_GA_ID?: string
}
}
三、类型安全 #
3.1 API路由类型 #
tsx
import { NextRequest, NextResponse } from 'next/server'
interface RouteContext {
params: Promise<{ id: string }>
}
export async function GET(
request: NextRequest,
{ params }: RouteContext
): Promise<NextResponse<ApiResponse<User>>> {
const { id } = await params
const user = await getUser(id)
return NextResponse.json({
data: user,
message: 'Success',
success: true,
})
}
3.2 Server Actions类型 #
tsx
'use server'
interface FormState {
error?: string
success?: boolean
}
interface FormData {
title: string
content: string
}
export async function createPost(
prevState: FormState,
formData: FormData
): Promise<FormState> {
try {
await db.post.create({ data: formData })
return { success: true }
} catch {
return { error: '创建失败' }
}
}
3.3 组件类型 #
tsx
import { ComponentProps, Ref } from 'react'
type ButtonBaseProps = ComponentProps<'button'>
interface CustomButtonProps extends ButtonBaseProps {
variant?: 'primary' | 'secondary'
loading?: boolean
}
export const Button = forwardRef<HTMLButtonElement, CustomButtonProps>(
({ variant = 'primary', loading, children, ...props }, ref) => {
return (
<button ref={ref} {...props}>
{loading ? <Spinner /> : children}
</button>
)
}
)
Button.displayName = 'Button'
四、泛型组件 #
4.1 列表组件 #
tsx
interface ListProps<T> {
items: T[]
renderItem: (item: T, index: number) => React.ReactNode
keyExtractor: (item: T) => string
emptyMessage?: string
}
export function List<T>({
items,
renderItem,
keyExtractor,
emptyMessage = '暂无数据',
}: ListProps<T>) {
if (items.length === 0) {
return <div className="text-center text-gray-500">{emptyMessage}</div>
}
return (
<ul>
{items.map((item, index) => (
<li key={keyExtractor(item)}>
{renderItem(item, index)}
</li>
))}
</ul>
)
}
4.2 表单组件 #
tsx
interface FormProps<T> {
defaultValues: T
onSubmit: (data: T) => void
children: (methods: FormMethods<T>) => React.ReactNode
}
export function Form<T extends Record<string, any>>({
defaultValues,
onSubmit,
children,
}: FormProps<T>) {
const methods = useForm({ defaultValues })
return (
<form onSubmit={methods.handleSubmit(onSubmit)}>
{children(methods)}
</form>
)
}
五、类型工具 #
5.1 工具类型 #
typescript
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
type Required<T, K extends keyof T> = Omit<T, K> & { [P in K]-?: T[P] }
type Readonly<T> = { readonly [P in keyof T]: T[P] }
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
}
5.2 API类型 #
typescript
type ApiSuccess<T> = {
success: true
data: T
}
type ApiError = {
success: false
error: string
}
type ApiResult<T> = ApiSuccess<T> | ApiError
六、最佳实践 #
6.1 避免any #
tsx
// 不推荐
function processData(data: any) {
return data.value
}
// 推荐
interface Data {
value: string
}
function processData(data: Data) {
return data.value
}
6.2 使用类型推断 #
tsx
const user = await getUser(id)
// TypeScript 自动推断 user 的类型
6.3 使用const断言 #
tsx
const ROUTES = {
HOME: '/',
LOGIN: '/login',
DASHBOARD: '/dashboard',
} as const
type Routes = typeof ROUTES[keyof typeof ROUTES]
七、总结 #
TypeScript集成要点:
| 要点 | 说明 |
|---|---|
| 配置 | tsconfig.json |
| Props类型 | 接口定义 |
| API类型 | 响应类型 |
| 环境变量 | 类型扩展 |
| 泛型组件 | 可复用性 |
下一步,让我们学习测试策略!
最后更新:2026-03-28