如何在d.ts文件声明类型并在.ts文件中无需导入即可获得类型安全(解决重复声明错误)
Hey there! Let's break down what's going wrong and how to fix this properly.
First off, that "Cannot redeclare block-scoped variable 'POST'" error makes total sense. You're declaring the POST export both in your actual route.ts implementation and in the ambient module declaration in generated.d.ts. TypeScript sees these as two conflicting definitions for the same module export, hence the conflict.
Your end goal is really smart—auto-generating types from Zod schemas and letting users get type safety without importing anything. Let's walk through the correct approaches to make this work, starting with fixing the duplicate declaration error.
Solution 1: Global Type Declarations (Simple & Direct)
The easiest way to avoid imports is to define your route-specific types in the global scope. This lets you reference them anywhere in your project without importing, while eliminating the duplicate declaration issue.
Step 1: Define Global Types in generated.d.ts
// src/types/generated.d.ts declare global { // Create a unique type for your route's request parameters type UsersRoutePOSTRequest = { body: { name: string }; }; } // Critical: This line turns the file into a valid module, enabling global augmentation export {};
Step 2: Use the Global Type in route.ts
Now you can annotate your POST function's parameter with the global type—no imports required:
// src/routes/users/route.ts export const POST = ({ body }: UsersRoutePOSTRequest) => { console.log(body.name); // ✅ Full type safety here! };
This fixes the duplicate declaration error (we're no longer re-declaring POST in the .d.ts) and gives you the automatic type safety you want.
Solution 2: JSDoc Annotations (Zero TypeScript Syntax in Routes)
If you want to keep your route files completely clean of TypeScript annotations, you can use JSDoc to reference the global types. TypeScript will pick up these comments automatically, giving you type safety without any extra syntax in your handler code.
Step 1: Reuse the Global Type from Solution 1
Keep the generated.d.ts file from the first solution—no changes needed here.
Step 2: Add JSDoc to route.ts
// src/routes/users/route.ts /** * @param {UsersRoutePOSTRequest} params */ export const POST = ({ body }) => { console.log(body.name); // ✅ Type safety works here too! };
This is perfect for users who want to write plain JavaScript-style code but still get TypeScript's type checking benefits.
Solution 3: Framework-Specific Module Augmentation (Advanced, Scalable)
If you're building on a framework (like Next.js, Express, or a custom one), you can use module augmentation to auto-infer types based on file paths. This is ideal for your auto-generated Zod use case at scale.
Step 1: Augment a Global Route Handler Interface
// src/types/generated.d.ts declare global { // Map file paths to their corresponding request types interface RouteTypeMap { "routes/users/route": { POST: { body: { name: string } }; }; // Add more routes here as you generate them } } export {};
Step 2: Wire Up to Your Framework's Handler Type
If your framework defines a base handler type (e.g., RouteHandler), you can modify it to infer types from the RouteTypeMap using the current file path. For example:
// src/types/framework.d.ts import type { RouteTypeMap } from "./generated"; // Infer the current file's path (you might need a helper for this) declare const __dirname: string; type CurrentRoutePath = Extract<keyof RouteTypeMap, string>; // Augment the framework's handler type to use the mapped type interface RouteHandler<T extends keyof RouteTypeMap = CurrentRoutePath> { (params: RouteTypeMap[T]["POST"]): void; }
Then your route handler gets type safety automatically, with zero annotations:
// src/routes/users/route.ts export const POST: RouteHandler = ({ body }) => { console.log(body.name); // ✅ Auto-inferred type safety! };
This approach is more complex but scales perfectly—you can write a script to convert your Zod schemas into entries in the RouteTypeMap automatically, and all your routes will pick up the correct types instantly.
Key Takeaway for Your Original Error
The main mistake in your initial code was re-declaring the POST export in both route.ts and generated.d.ts. By shifting to global types or module augmentation (instead of re-declaring exports), you eliminate that conflict entirely.
For your auto-generated Zod workflow:
- Generate unique global types (or
RouteTypeMapentries) from each Zod schema. - Either annotate handlers with these types, use JSDoc, or leverage framework augmentation for full automation.
- Users get complete type safety without ever importing a single type.
内容来源于stack exchange

