Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Adjust fail method and ActionFailure type #11260

Merged
merged 3 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/swift-clocks-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

fix: Adjust fail method and ActionFailure type
2 changes: 1 addition & 1 deletion packages/kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
],
"scripts": {
"lint": "prettier --config ../../.prettierrc --check .",
"check": "tsc",
"check": "tsc && cd ./test/types && tsc",
"check:all": "tsc && pnpm -r --filter=\"./**\" check",
"format": "prettier --config ../../.prettierrc --write .",
"test": "pnpm test:unit && pnpm test:integration",
Expand Down
21 changes: 19 additions & 2 deletions packages/kit/src/exports/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,31 @@ export function text(body, init) {
});
}

/**
* Create an `ActionFailure` object.
* @param {number} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
* @overload
* @param {number} status
* @returns {import('./public.js').ActionFailure<undefined>}
*/
/**
* Create an `ActionFailure` object.
* @template {Record<string, unknown> | undefined} [T=undefined]
* @param {number} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
* @param {T} [data] Data associated with the failure (e.g. validation errors)
* @returns {ActionFailure<T>}
* @param {T} data Data associated with the failure (e.g. validation errors)
* @overload
* @param {number} status
* @param {T} data
* @returns {import('./public.js').ActionFailure<T>}
*/
/**
* Create an `ActionFailure` object.
* @param {number} status
* @param {any} [data]
* @returns {import('./public.js').ActionFailure<any>}
*/
export function fail(status, data) {
// @ts-expect-error unique symbol missing
return new ActionFailure(status, data);
}

Expand Down
10 changes: 8 additions & 2 deletions packages/kit/src/exports/public.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@ import {
RequestOptions,
RouteSegment
} from '../types/private.js';
import { ActionFailure } from '../runtime/control.js';
import { BuildData, SSRNodeLoader, SSRRoute, ValidatedConfig } from 'types';
import type { PluginOptions } from '@sveltejs/vite-plugin-svelte';

export { PrerenderOption } from '../types/private.js';
export { ActionFailure };

/**
* [Adapters](https://kit.svelte.dev/docs/adapters) are responsible for taking the production build and turning it into something that can be deployed to a platform of your choosing.
Expand Down Expand Up @@ -58,6 +56,14 @@ type OptionalUnion<
A extends keyof U = U extends U ? keyof U : never
> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;

declare const uniqueSymbol: unique symbol;

export interface ActionFailure<T extends Record<string, unknown> | undefined = undefined> {
status: number;
data: T;
[uniqueSymbol]: true; // necessary or else UnpackValidationError could wrongly unpack objects with the same shape as ActionFailure
}

type UnpackValidationError<T> = T extends ActionFailure<infer X>
? X
: T extends void
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/runtime/control.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class Redirect {
export class ActionFailure {
/**
* @param {number} status
* @param {T} [data]
* @param {T} data
*/
constructor(status, data) {
this.status = status;
Expand Down
17 changes: 16 additions & 1 deletion packages/kit/test/types/actions.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import Kit from '@sveltejs/kit';
import * as Kit from '@sveltejs/kit';

// Test: Action types inferred correctly and transformed into a union
type Actions = {
foo: () => Promise<void>;
bar: () => Promise<{ success: boolean } | Kit.ActionFailure<{ message: string }>>;
baz: () => Kit.ActionFailure<{ foo: string }> | { status: number; data: string };
};

let form: Kit.AwaitedActions<Actions> = null as any;
Expand All @@ -23,3 +24,17 @@ form2.message = '';
form2.success = true;
// @ts-expect-error - cannot both be present at the same time
form2 = { message: '', success: true };

// Test: ActionFailure is correctly infered to be different from the normal return type even if they have the same shape
type Actions3 = {
bar: () => Kit.ActionFailure<{ foo: string }> | { status: number; data: { bar: string } };
};
let form3: Kit.AwaitedActions<Actions3> = null as any;
form3.foo = '';
form3.status = 200;
// @ts-expect-error - cannot both be present at the same time
form3 = { foo: '', status: 200 };

let foo: any = null;
// @ts-expect-error ActionFailure is not a class and so you can't do instanceof
foo instanceof Kit.ActionFailure;
18 changes: 5 additions & 13 deletions packages/kit/test/types/load.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
import Kit, { Deferred } from '@sveltejs/kit';
import * as Kit from '@sveltejs/kit';

// Test: Return types inferred correctly and transformed into a union
type LoadReturn1 = { success: true } | { message: Promise<string> };
type LoadReturn1 =
| { success: true; message?: undefined }
| { success?: undefined; message: string };

let result1: Kit.AwaitedProperties<LoadReturn1> = null as any;
let result1: Kit.LoadProperties<LoadReturn1> = null as any;
result1.message = '';
result1.success = true;
// @ts-expect-error - cannot both be present at the same time
result1 = { message: '', success: true };

// Test: Return types keep promise for Deferred
type LoadReturn2 = { success: true } | Deferred<{ message: Promise<string>; eager: true }>;

let result2: Kit.AwaitedProperties<LoadReturn2> = null as any;
result2.message = Promise.resolve('');
result2.eager = true;
result2.success = true;
// @ts-expect-error - cannot both be present at the same time
result2 = { message: '', success: true };
52 changes: 29 additions & 23 deletions packages/kit/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ declare module '@sveltejs/kit' {
A extends keyof U = U extends U ? keyof U : never
> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;

const uniqueSymbol: unique symbol;

export interface ActionFailure<T extends Record<string, unknown> | undefined = undefined> {
status: number;
data: T;
[uniqueSymbol]: true; // necessary or else UnpackValidationError could wrongly unpack objects with the same shape as ActionFailure
}

type UnpackValidationError<T> = T extends ActionFailure<infer X>
? X
: T extends void
Expand Down Expand Up @@ -1494,28 +1502,6 @@ declare module '@sveltejs/kit' {
}

type TrailingSlash = 'never' | 'always' | 'ignore';
class HttpError_1 {

constructor(status: number, body: {
message: string;
} extends App.Error ? (App.Error | string | undefined) : App.Error);
status: number;
body: App.Error;
toString(): string;
}
class Redirect_1 {

constructor(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308, location: string);
status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308;
location: string;
}

export class ActionFailure<T extends Record<string, unknown> | undefined = undefined> {

constructor(status: number, data?: T | undefined);
status: number;
data: T | undefined;
}
interface Asset {
file: string;
size: number;
Expand Down Expand Up @@ -1739,12 +1725,17 @@ declare module '@sveltejs/kit' {
* @param init Options such as `status` and `headers` that will be added to the response. A `Content-Length` header will be added automatically.
*/
export function text(body: string, init?: ResponseInit | undefined): Response;
/**
* Create an `ActionFailure` object.
* @param status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
* */
export function fail(status: number): ActionFailure<undefined>;
/**
* Create an `ActionFailure` object.
* @param status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
* @param data Data associated with the failure (e.g. validation errors)
* */
export function fail<T extends Record<string, unknown> | undefined = undefined>(status: number, data?: T | undefined): ActionFailure<T>;
export function fail<T extends Record<string, unknown> | undefined = undefined>(status: number, data: T): ActionFailure<T>;
/**
* Populate a route ID with params to resolve a pathname.
* @example
Expand All @@ -1762,6 +1753,21 @@ declare module '@sveltejs/kit' {
export type LessThan<TNumber extends number, TArray extends any[] = []> = TNumber extends TArray['length'] ? TArray[number] : LessThan<TNumber, [...TArray, TArray['length']]>;
export type NumericRange<TStart extends number, TEnd extends number> = Exclude<TEnd | LessThan<TEnd>, LessThan<TStart>>;
export const VERSION: string;
class HttpError_1 {

constructor(status: number, body: {
message: string;
} extends App.Error ? (App.Error | string | undefined) : App.Error);
status: number;
body: App.Error;
toString(): string;
}
class Redirect_1 {

constructor(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308, location: string);
status: 301 | 302 | 303 | 307 | 308 | 300 | 304 | 305 | 306;
location: string;
}
}

declare module '@sveltejs/kit/hooks' {
Expand Down