Skip to content

Commit 9d726f1

Browse files
allow InputProps and OutputProps to be typed in generated Load (#4305)
* add missing `extends` to `params` and `Props` generics * allow `InputProps` and `OutputProps` to be typed in generated `Load` * add changeset * update docs * allow `Output` to be typed in generated `RequestHandler` * format * fix * expose Body as ResponseBody * neaten up code generation a bit Co-authored-by: Rich Harris <[email protected]>
1 parent fcfd947 commit 9d726f1

File tree

6 files changed

+69
-33
lines changed

6 files changed

+69
-33
lines changed

.changeset/calm-ravens-tell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
[breaking] allow `InputProps` and `OutputProps` to be typed separately in generated `Load`

.changeset/little-toys-camp.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
allow `Output` to be typed in generated `RequestHandler`

documentation/docs/14-types.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ export type RequestHandler<Body = any> = GenericRequestHandler<
3535
Body
3636
>;
3737

38-
export type Load<Props = Record<string, any>> = GenericLoad<
39-
{ foo: string; bar: string; baz: string },
40-
Props
41-
>;
38+
export type Load<
39+
InputProps extends Record<string, any> = Record<string, any>,
40+
OutputProps extends Record<string, any> = InputProps
41+
> = GenericLoad<{ foo: string; bar: string; baz: string }, InputProps, OutputProps>
4242
```
4343
4444
These files can be imported into your endpoints and pages as siblings, thanks to the [`rootDirs`](https://www.typescriptlang.org/tsconfig#rootDirs) option in your TypeScript configuration:

packages/kit/src/core/sync/write_types.js

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
import { write_if_changed } from './utils.js';
22

3+
/** @param {string} imports */
4+
const header = (imports) => `
5+
// this file is auto-generated
6+
import type { ${imports} } from '@sveltejs/kit';`;
7+
8+
/** @param {string} arg */
9+
const endpoint = (arg) => `
10+
export type RequestHandler<Output extends ResponseBody = ResponseBody> = GenericRequestHandler<${arg}, Output>;`;
11+
12+
/** @param {string} arg */
13+
const page = (arg) => `
14+
export type Load<
15+
InputProps extends Record<string, any> = Record<string, any>,
16+
OutputProps extends Record<string, any> = InputProps
17+
> = GenericLoad<${arg}, InputProps, OutputProps>;`;
18+
319
/**
420
* @param {import('types').ValidatedConfig} config
521
* @param {import('types').ManifestData} manifest_data
@@ -53,24 +69,24 @@ export function write_types(config, manifest_data) {
5369
const arg =
5470
params.length > 0 ? `{ ${params.map((param) => `${param}: string`).join('; ')} }` : '{}';
5571

56-
const imports = [
57-
type !== 'page' && 'RequestHandler as GenericRequestHandler',
58-
type !== 'endpoint' && 'Load as GenericLoad'
59-
]
60-
.filter(Boolean)
61-
.join(', ');
62-
63-
const file = `${config.kit.outDir}/types/${key || 'index'}.d.ts`;
64-
const content = [
65-
'// this file is auto-generated',
66-
`import type { ${imports} } from '@sveltejs/kit';`,
67-
type !== 'page' && `export type RequestHandler = GenericRequestHandler<${arg}>;`,
68-
type !== 'endpoint' &&
69-
`export type Load<Props = Record<string, any>> = GenericLoad<${arg}, Props>;`
70-
]
71-
.filter(Boolean)
72-
.join('\n');
73-
74-
write_if_changed(file, content);
72+
const imports = [];
73+
const content = [];
74+
75+
if (type !== 'page') {
76+
imports.push('RequestHandler as GenericRequestHandler, ResponseBody');
77+
content.push(endpoint(arg));
78+
}
79+
80+
if (type !== 'endpoint') {
81+
imports.push('Load as GenericLoad');
82+
content.push(page(arg));
83+
}
84+
85+
content.unshift(header(imports.join(', ')));
86+
87+
write_if_changed(
88+
`${config.kit.outDir}/types/${key || 'index'}.d.ts`,
89+
content.join('\n').trim()
90+
);
7591
});
7692
}

packages/kit/types/index.d.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import './ambient';
66
import { CompileOptions } from 'svelte/types/compiler/interfaces';
77
import {
88
AdapterEntry,
9-
Body,
109
CspDirectives,
1110
Either,
1211
ErrorLoadInput,
1312
Fallthrough,
13+
JSONValue,
1414
LoadInput,
1515
LoadOutput,
1616
Logger,
@@ -158,7 +158,10 @@ export interface Config {
158158
preprocess?: any;
159159
}
160160

161-
export interface ErrorLoad<Params = Record<string, string>, Props = Record<string, any>> {
161+
export interface ErrorLoad<
162+
Params extends Record<string, string> = Record<string, string>,
163+
Props extends Record<string, any> = Record<string, any>
164+
> {
162165
(input: ErrorLoadInput<Params>): MaybePromise<LoadOutput<Props>>;
163166
}
164167

@@ -218,11 +221,14 @@ export interface Page<Params extends Record<string, string> = Record<string, str
218221
* Note that you can use [generated types](/docs/types#generated-types)
219222
* instead of manually specifying the `Params` generic argument.
220223
*/
221-
export interface RequestHandler<Params = Record<string, string>, Output extends Body = Body> {
224+
export interface RequestHandler<
225+
Params extends Record<string, string> = Record<string, string>,
226+
Output extends ResponseBody = ResponseBody
227+
> {
222228
(event: RequestEvent<Params>): RequestHandlerOutput<Output>;
223229
}
224230

225-
export type RequestHandlerOutput<Output extends Body = Body> = MaybePromise<
231+
export type RequestHandlerOutput<Output extends ResponseBody = ResponseBody> = MaybePromise<
226232
Either<
227233
{
228234
status?: number;
@@ -233,6 +239,8 @@ export type RequestHandlerOutput<Output extends Body = Body> = MaybePromise<
233239
>
234240
>;
235241

242+
export type ResponseBody = JSONValue | Uint8Array | ReadableStream | import('stream').Readable;
243+
236244
export class Server {
237245
constructor(manifest: SSRManifest);
238246
respond(request: Request, options: RequestOptions): Promise<Response>;

packages/kit/types/private.d.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ export interface AdapterEntry {
2828
}) => void;
2929
}
3030

31-
export type Body = JSONValue | Uint8Array | ReadableStream | import('stream').Readable;
32-
3331
// Based on https://github.com/josh-hemphill/csp-typed-directives/blob/latest/src/csp.types.ts
3432
//
3533
// MIT License
@@ -140,7 +138,8 @@ export interface CspDirectives {
140138

141139
export type Either<T, U> = Only<T, U> | Only<U, T>;
142140

143-
export interface ErrorLoadInput<Params = Record<string, string>> extends LoadInput<Params> {
141+
export interface ErrorLoadInput<Params extends Record<string, string> = Record<string, string>>
142+
extends LoadInput<Params> {
144143
status?: number;
145144
error?: Error;
146145
}
@@ -165,7 +164,10 @@ export type JSONValue =
165164
| JSONValue[]
166165
| JSONObject;
167166

168-
export interface LoadInput<Params = Record<string, string>, Props = Record<string, any>> {
167+
export interface LoadInput<
168+
Params extends Record<string, string> = Record<string, string>,
169+
Props extends Record<string, any> = Record<string, any>
170+
> {
169171
url: URL;
170172
params: Params;
171173
props: Props;
@@ -174,7 +176,7 @@ export interface LoadInput<Params = Record<string, string>, Props = Record<strin
174176
stuff: Partial<App.Stuff>;
175177
}
176178

177-
export interface LoadOutput<Props = Record<string, any>> {
179+
export interface LoadOutput<Props extends Record<string, any> = Record<string, any>> {
178180
status?: number;
179181
error?: string | Error;
180182
redirect?: string;
@@ -233,7 +235,7 @@ export interface PrerenderErrorHandler {
233235

234236
export type PrerenderOnErrorValue = 'fail' | 'continue' | PrerenderErrorHandler;
235237

236-
export interface RequestEvent<Params = Record<string, string>> {
238+
export interface RequestEvent<Params extends Record<string, string> = Record<string, string>> {
237239
clientAddress: string;
238240
locals: App.Locals;
239241
params: Params;

0 commit comments

Comments
 (0)