背景
在学习 nextjs 的时候,经常会遇到导出类型和类型擦除的相关问题
基础内容-导出与导入
可以这么理解,每一个 .ts 的文件都是一个代码库,如果不 export 的话他就是私有的,外部不能使用。
导出(export)
- 具名导出(Named export):一个文件可以有多个带有名字的导出
export const age = 20;export function L1ngg() { ... }- 默认导出(Default export):一个文件只能有一个默认导出
export default function defaultL1ngg() { ... }导入(import)
- 对于
Named export,必须使用{}且名字要对应:import { age , L1ngg } from xxx。 - 对于
Default export,可以随便取名:import randomName from xxx。
TS特色之值与类型的混合传递
这是ts语法特殊的地方,可以同时导出具体的变量(运行时候的值)和接口(编译时候的类型)。
export const PI = 3.14;export interface CircleConfig { radius: number;}当导入的时候就可以混合在一起导入
import { PI, CircleConfig } from './math'
关于 import type
当 TS 编译为 JS 的时候,所有的类型将会消失。如果仅仅是为了获取类型信息(比如给某个变量标记为interface),并不需要运行时的代码,TS 建议使用 import type。
示例:
//TS 源码import type { UserInterface } from "./types";import { userService } from "./services";
const user: UserInterface = userService.get();编译后的 JS
// UserInterface 的导入直接消失了,因为它只是个类型import { userService } from './services';
var user = userService.get();💡 深入了解类型擦除:想要深入理解类型擦除的工作原理、哪些内容会被擦除、哪些会保留,以及如何正确处理运行时类型检查?请阅读:TypeScript 类型擦除深入理解
为什么要使用 import type
使用 import type 有以下几个重要好处:
- 打包优化:避免将仅用于类型标注的模块打包进最终代码,减小打包体积
- 明确意图:让其他开发者一眼看出这是纯类型导入,提高代码可读性
- 避免副作用:某些模块在导入时会执行代码(如注册全局变量),使用
import type可以避免这些副作用 - 解决循环依赖:在某些循环依赖场景下,
import type可以打破循环引用
export type 的使用
与 import type 对应,导出也可以使用 export type 来明确只导出类型:
// types.ts - 只导出类型export type { UserInterface, AdminInterface } from "./interfaces";
// services.ts - 混合导出类型和值export { userService } from "./user-service";export type { UserInterface } from "./interfaces";内联类型导入(TS 3.8+)
TypeScript 3.8 引入了更灵活的语法,可以在同一个 import 语句中混合导入类型和值:
// 新语法:在花括号内使用 type 关键字标记类型import { userService, type UserInterface, type AdminInterface } from "./module";
// 等价于分开写(旧语法)import type { UserInterface, AdminInterface } from "./module";import { userService } from "./module";这种写法的优点是代码更简洁优雅,特别是当需要从同一个模块导入多个类型和值时。
常见错误
CAUTION使用
import type时需要注意以下几点:
1. 不能对 import type 导入的内容做运行时操作
import type { User } from "./types";
// ❌ 错误:User 在运行时不存在,无法实例化const user = new User();
// ✅ 正确:只用于类型标注const user: User = { name: "L1ngg", age: 20 };2. 类(Class)既是类型也是值
export class User { constructor(public name: string) {}}
// 如果使用 import type 导入类import type { User } from "./user";const user: User = { name: "L1ngg" }; // ✅ 可以用作类型const user2 = new User("L1ngg"); // ❌ 错误:运行时不存在
// 如果需要实例化,必须使用普通 importimport { User } from "./user";const user = new User("L1ngg"); // ✅ 正确在 Next.js 中的应用
在 Next.js 项目中,正确使用 import type 尤为重要,特别是在处理服务端组件和客户端组件的边界时。
1. 服务端和客户端组件的类型共享
// types/user.ts - 共享类型定义export interface UserProfile { id: string; name: string; email: string;}
// app/components/UserCard.tsx - 客户端组件"use client";import type { UserProfile } from "@/types/user"; // 只导入类型
export function UserCard({ user }: { user: UserProfile }) { return <div>{user.name}</div>;}2. Server Actions 中的类型传递
// app/actions/user.ts - Server Action"use server";import type { UserProfile } from "@/types/user";
export async function updateUser(data: UserProfile) { // 服务端逻辑 return { success: true };}
// app/components/UserForm.tsx - 客户端组件("use client");import { updateUser } from "@/app/actions/user";import type { UserProfile } from "@/types/user";
export function UserForm() { const handleSubmit = async (data: UserProfile) => { await updateUser(data); }; // ...}3. TypeScript 配置建议
在 Next.js 项目中,建议在 tsconfig.json 中开启以下配置:
{ "compilerOptions": { "isolatedModules": true, // Next.js 推荐开启,确保每个文件都能独立编译 "verbatimModuleSyntax": true // TS 5.0+ 新选项,更严格地检查导入导出 }}这些配置可以帮助你更早地发现类型导入的问题,避免运行时错误。
部分信息可能已经过时









