diff --git a/.changeset/swift-clocks-kneel.md b/.changeset/swift-clocks-kneel.md new file mode 100644 index 000000000000..493107153a90 --- /dev/null +++ b/.changeset/swift-clocks-kneel.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: Adjust fail method and ActionFailure type diff --git a/packages/kit/package.json b/packages/kit/package.json index 6e691d6cba8c..2235e8f99048 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -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", diff --git a/packages/kit/src/exports/index.js b/packages/kit/src/exports/index.js index 6dc9d09f4ee6..1f444c765e87 100644 --- a/packages/kit/src/exports/index.js +++ b/packages/kit/src/exports/index.js @@ -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} + */ /** * Create an `ActionFailure` object. * @template {Record | 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} + * @param {T} data Data associated with the failure (e.g. validation errors) + * @overload + * @param {number} status + * @param {T} data + * @returns {import('./public.js').ActionFailure} + */ +/** + * Create an `ActionFailure` object. + * @param {number} status + * @param {any} [data] + * @returns {import('./public.js').ActionFailure} */ export function fail(status, data) { + // @ts-expect-error unique symbol missing return new ActionFailure(status, data); } diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index d196defdae40..8f96d669b665 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -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. @@ -58,6 +56,14 @@ type OptionalUnion< A extends keyof U = U extends U ? keyof U : never > = U extends unknown ? { [P in Exclude]?: never } & U : never; +declare const uniqueSymbol: unique symbol; + +export interface ActionFailure | 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 extends ActionFailure ? X : T extends void diff --git a/packages/kit/src/runtime/control.js b/packages/kit/src/runtime/control.js index d27725429038..618d18df5d14 100644 --- a/packages/kit/src/runtime/control.js +++ b/packages/kit/src/runtime/control.js @@ -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; diff --git a/packages/kit/test/types/actions.test.ts b/packages/kit/test/types/actions.test.ts index 8ca089d18c28..ab0debf3ebf7 100644 --- a/packages/kit/test/types/actions.test.ts +++ b/packages/kit/test/types/actions.test.ts @@ -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; bar: () => Promise<{ success: boolean } | Kit.ActionFailure<{ message: string }>>; + baz: () => Kit.ActionFailure<{ foo: string }> | { status: number; data: string }; }; let form: Kit.AwaitedActions = null as any; @@ -23,3 +24,13 @@ 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 = null as any; +form3.foo = ''; +form3.status = 200; +// @ts-expect-error - cannot both be present at the same time +form3 = { foo: '', status: 200 }; diff --git a/packages/kit/test/types/load.test.ts b/packages/kit/test/types/load.test.ts index 321d70931bdd..10cc108af530 100644 --- a/packages/kit/test/types/load.test.ts +++ b/packages/kit/test/types/load.test.ts @@ -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 }; +type LoadReturn1 = + | { success: true; message?: undefined } + | { success?: undefined; message: string }; -let result1: Kit.AwaitedProperties = null as any; +let result1: Kit.LoadProperties = 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; eager: true }>; - -let result2: Kit.AwaitedProperties = 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 };