diff --git a/.changeset/large-cows-suffer.md b/.changeset/large-cows-suffer.md new file mode 100644 index 000000000000..49cd334deeaa --- /dev/null +++ b/.changeset/large-cows-suffer.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/kit": patch +--- + +delegate `RequestHandler` generics `Body` validation diff --git a/packages/kit/src/core/sync/write_types.js b/packages/kit/src/core/sync/write_types.js index 329bb68d563b..b1da50b81e3e 100644 --- a/packages/kit/src/core/sync/write_types.js +++ b/packages/kit/src/core/sync/write_types.js @@ -8,7 +8,7 @@ import type { ${imports} } from '@sveltejs/kit';`; /** @param {string} arg */ const endpoint = (arg) => ` -export type RequestHandler = GenericRequestHandler<${arg}, Output>;`; +export type RequestHandler = GenericRequestHandler<${arg}, Output>;`; /** @param {string} arg */ const page = (arg) => ` diff --git a/packages/kit/test/typings/endpoint.test.ts b/packages/kit/test/typings/endpoint.test.ts index 5fcb44ce8e31..4fa6cb2a715a 100644 --- a/packages/kit/test/typings/endpoint.test.ts +++ b/packages/kit/test/typings/endpoint.test.ts @@ -68,30 +68,53 @@ export const differential_headers_assignment: RequestHandler = () => { } }; -// TODO https://github.com/sveltejs/kit/issues/1997 -// interface ExamplePost { -// title: string; -// description: string; -// published_date?: string; -// author_name?: string; -// author_link?: string; -// } -// // valid - should not be any different -// export const generic_case: RequestHandler, ExamplePost> = () => { -// return { -// body: {} as ExamplePost -// }; -// }; +/** + * NOTE about type casting in body returned + * + * tests below with `{} as Interface` casts are there only for + * convenience purposes so we won't have to actually fill in the + * required data, it serves the exact same purpose and doesn't + * make the tests invalid + */ + +/** example json-serializable POJO */ +interface ExamplePost { + title: string; + description: string; + published_date?: string; + author_name?: string; + author_link?: string; +} +// valid - should not be any different +export const generic_case: RequestHandler, ExamplePost> = () => { + return { + body: {} as ExamplePost + }; +}; // --- invalid cases --- // @ts-expect-error - body must be JSON serializable -export const error_body_must_be_serializable: RequestHandler = () => { +export const error_unserializable_literal: RequestHandler = () => { return { body: () => {} }; }; +/** example object that isn't serializable */ +interface InvalidPost { + sorter(a: any, b: any): number; +} +// @ts-expect-error - body must be JSON serializable with Generic passed +export const error_unserializable_generic: RequestHandler< + Record, + InvalidPost +> = () => { + return { + body: {} as InvalidPost + }; +}; + // @ts-expect-error - body typed array must only be Uint8Array export const error_other_typed_array_instances: RequestHandler = () => { return { diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index ead4ff5f4d13..f260a223b43f 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -6,6 +6,7 @@ import './ambient'; import { CompileOptions } from 'svelte/types/compiler/interfaces'; import { AdapterEntry, + BodyValidator, CspDirectives, JSONValue, Logger, @@ -241,15 +242,15 @@ export interface ParamMatcher { */ export interface RequestHandler< Params extends Record = Record, - Output extends ResponseBody = ResponseBody + Output = ResponseBody > { (event: RequestEvent): MaybePromise>; } -export interface RequestHandlerOutput { +export interface RequestHandlerOutput { status?: number; headers?: Headers | Partial; - body?: Output; + body?: Output extends ResponseBody ? Output : BodyValidator; } export type ResponseBody = JSONValue | Uint8Array | ReadableStream | import('stream').Readable; diff --git a/packages/kit/types/private.d.ts b/packages/kit/types/private.d.ts index ad2ac7840fd1..ddf476d80a96 100644 --- a/packages/kit/types/private.d.ts +++ b/packages/kit/types/private.d.ts @@ -26,6 +26,10 @@ export interface AdapterEntry { }) => MaybePromise; } +export type BodyValidator = { + [P in keyof T]: T[P] extends JSONValue ? BodyValidator : never; +}; + // Based on https://github.com/josh-hemphill/csp-typed-directives/blob/latest/src/csp.types.ts // // MIT License