diff --git a/src/build.ts b/src/build.ts index db4479162f..da9d47665b 100644 --- a/src/build.ts +++ b/src/build.ts @@ -109,6 +109,7 @@ export async function build(nitro: Nitro) { export async function writeTypes(nitro: Nitro) { const types: NitroTypes = { routes: {}, + handlers: {} }; const typesDir = resolve(nitro.options.buildDir, "types"); @@ -131,6 +132,12 @@ export async function writeTypes(nitro: Nitro) { types.routes[mw.route][method]!.push( `Simplify>>>` ); + + types.handlers[mw.route] ??= {}; + types.handlers[mw.route][method] ??= []; + types.handlers[mw.route][method]!.push( + `typeof import('${relativePath}').default` + ); } let autoImportedTypes: string[] = []; @@ -208,6 +215,17 @@ export async function writeTypes(nitro: Nitro) { ].join("\n") ), " }", + " interface InternalApiHandlers {", + ...Object.entries(types.handlers).map(([path, methods]) => + [ + ` '${path}': {`, + ...Object.entries(methods).map( + ([method, types]) => ` '${method}': ${types.join(" | ")}` + ), + " }", + ].join("\n") + ), + " }", "}", // Makes this a module for augmentation purposes "export {}", diff --git a/src/types/fetch.ts b/src/types/fetch.ts index a87d9c7c5b..3f93edd492 100644 --- a/src/types/fetch.ts +++ b/src/types/fetch.ts @@ -1,10 +1,10 @@ -import type { RouterMethod } from "h3"; +import type { EventHandler, EventHandlerRequest, RouterMethod } from "h3"; import type { FetchRequest, FetchOptions, FetchResponse } from "ofetch"; import type { MatchedRoutes } from "./utils"; -// An interface to extend in a local project - +// Interfaces to extend in a local project export interface InternalApi {} +export interface InternalApiHandlers {} export type NitroFetchRequest = | Exclude @@ -49,14 +49,40 @@ export type AvailableRouterMethod = : RouterMethod : RouterMethod; -// Argumented fetch options to include the correct request methods. -// This overrides the default, which is only narrowed to a string. -export interface NitroFetchOptions< +// Extracts the body or query parameter types expected for a route +// based on the types inferred from bodyValidator/queryValidator. +export type HandlerInputType< + R extends NitroFetchRequest, + M extends AvailableRouterMethod, + P extends "body" | "query" +> = + R extends string + ? keyof InternalApiHandlers[MatchedRoutes] extends undefined + ? any + : InternalApiHandlers[MatchedRoutes][ + Extract< + keyof InternalApiHandlers[MatchedRoutes], + "default" + > extends undefined ? M : "default" + ] extends EventHandler> + ? P extends "query" ? Query : Body + : any + : any; + +// Argumented fetch options to include the correct request methods, +// body types, and query parameter types. +export type NitroFetchOptions< R extends NitroFetchRequest, M extends AvailableRouterMethod = AvailableRouterMethod, -> extends FetchOptions { - method?: Uppercase | M; -} +> = Omit & ( + M extends string + ? { + method?: Uppercase | M; + body?: HandlerInputType; + query?: HandlerInputType; + } + : never + ); // Extract the route method from options which might be undefined or without a method parameter. export type ExtractedRouteMethod< diff --git a/src/types/nitro.ts b/src/types/nitro.ts index ae731a53cb..f0542811fc 100644 --- a/src/types/nitro.ts +++ b/src/types/nitro.ts @@ -83,9 +83,10 @@ export type PrerenderGenerateRoute = PrerenderRoute; type HookResult = void | Promise; -export type NitroTypes = { - routes: Record>>; -}; +export type NitroTypes = Record< + 'routes' | 'handlers', + Record>> +>; export interface NitroHooks { "types:extend": (types: NitroTypes) => HookResult;