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

export ValidationError interface #6885

Merged

Conversation

david-plugge
Copy link
Contributor

Allow typing the return type of an action (example)

Please don't delete this checklist! Before submitting the PR, please make sure you do the following:

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.

Tests

  • Run the tests with pnpm test and lint the project with pnpm lint and pnpm check

Changesets

  • If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running pnpm changeset and following the prompts. All changesets should be patch until SvelteKit 1.0

@changeset-bot
Copy link

changeset-bot bot commented Sep 19, 2022

⚠️ No Changeset found

Latest commit: 9bfed3e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@david-plugge
Copy link
Contributor Author

@dummdidumm do you think #6869 (comment) is worth considering? It´s an edge case but i don´t think a helper property like __sveltekit_validation_error__: unknown has any downside.

@dummdidumm
Copy link
Member

I think that's worth considering, yes, but it should probably be a symbol so you don't see it during autocompletion.
Generally, @Rich-Harris has been hesitant to export these classes, not sure if this use case changes his mind - probably will be something along the lines of "implement isValidationError function instead". Let's see.

@david-plugge
Copy link
Contributor Author

to export these classes

No need to export the classes, the interface is enough for that use case.

@dummdidumm
Copy link
Member

Ah sry, misunderstood - it's purely about typing, not about an instanceof check. So yes that would be ok in that regard, although it may be weird at this point to not be able to do an instancof check with something that is imported as the same name as the class.

@david-plugge
Copy link
Contributor Author

I´d be fine with that but i wonder why the classes should not be exported?

@Rich-Harris
Copy link
Member

i wonder why the classes should not be exported?

Just conservatism — the less we expose, the more flexibility we have in future to make changes to how things work under the hood — and a preference for only having one way to do things. In other words it shouldn't be possible to do both of these...

import { invalid } from '@sveltejs/kit';

// ...
return invalid(400, {...});
import { ValidationError } from '@sveltejs/kit';

// ...
return new ValidationError(400, {...});

...because that's needlessly confusing, and the function is more ergonomic and flexible than the class.

I'm not sure I understand what's going on with the validationErrorSymbol stuff. I wonder if we could just make invalid generic and do this instead?

-Promise<{ success: true } | ValidationError<FormValidationError>>
+Promise<{ success: true } | ReturnType<typeof invalid<400, FormValidationError>>>

@dummdidumm
Copy link
Member

The validationErrorSymbol stuff is somewhat orthogonal. Typescript does structural typing so in theory you can return a success with json({status, body}) and TypeScript would say "oh that's the shape of the ValidationError" and the type unpacking logic would be wrong

@dummdidumm
Copy link
Member

Another argument in favor of exporting the interface is #6933

@dummdidumm dummdidumm mentioned this pull request Sep 21, 2022
5 tasks
@david-plugge
Copy link
Contributor Author

david-plugge commented Sep 21, 2022

+Promise<{ success: true } | ReturnType<typeof invalid<400, FormValidationError>>>

The status as a Generic is actually pretty nice, that way it´s possible to define return types for a specific status code.

import type { Actions } from '@sveltejs/kit';

interface ValidationError<S extends number, T extends Record<string, unknown>> {
	status: S;
	data: T;
}

function invalid<S extends number, T extends Record<string, unknown>>(
	status: S,
	data: T
): ValidationError<S, T> {
	return new ValidationError(status, data);
}

export const actions: Actions = {
	default: async ({
		url
	}): Promise<{ success: true } | ValidationError<400, { valueMissing: true }> | ValidationError<409, { alreadyExists: true }>> => {
		if (!requestValid()) {
			return invalid(400, {
				valueMissing: true
			});
		}

		if (resourceAlreadyExists()) {
			return invalid(409, {
				alreadyExists: true
			});
		}

		return {
			success: true
		};
	}
};

You can pass number if you want to use any status code.

packages/kit/types/index.d.ts Outdated Show resolved Hide resolved
@dummdidumm
Copy link
Member

Before merging this I wanted to first confirm the behavior in #6933 - @ivanhofer could you provide a minimum reproducible (or a code snippet if that's enough)? I can't reproduce this.

@ivanhofer
Copy link
Contributor

@dummdidumm I have tested it with the changes from this branch and the issue is fixed.

This is the content of the stripped down proxypage.server.ts:

// @ts-nocheck
import { invalid } from '@sveltejs/kit'
import type { Actions } from './$types.js'

export const actions = {
	async default() {
		return invalid(400)
	},
}
	; null as any as Actions;

It seems this issue only occurs when setting composite to true
tsconfig.json

{
	"extends": "./.svelte-kit/tsconfig.json",
	"compilerOptions": {
		"composite": true,
	}
}

@dummdidumm
Copy link
Member

dummdidumm commented Sep 21, 2022

Interesting that // @ts-nocheck has no effect here, but I guess that makes sense because TypeScript doesn't know if that exported variable isn't used in another project (composite). This may potentially also affect other types, let's see what the future holds ..

@david-plugge
Copy link
Contributor Author

david-plugge commented Sep 21, 2022

	; null as any as Actions;

Why is this always included in the generated proxy file? Seems like some nonesense 😄

Copy link
Member

@Rich-Harris Rich-Harris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to figure out a way to prevent this from showing up in the docs before we can merge this

https://kit-svelte-m4bayg7p6-svelte.vercel.app/docs/modules#do-not-use

image

@david-plugge
Copy link
Contributor Author

What about moving validationErrorSymbol over to the private types?
The explanation in the additional-types section should be enough

@david-plugge
Copy link
Contributor Author

david-plugge commented Sep 21, 2022

A more general solution

// private.d.ts

/**
 * This doesn't actually exist, it's a way to better distinguish the type
 */
const uniqueSymbol: unique symbol;

export interface UniqueInterface {
	readonly [uniqueSymbol]: unknown;
}

// index.d.ts

// Needs to be here, else ActionData will be resolved to unknown - probably because of "d.ts file imports .js file" in combination with allowJs
export interface ValidationError<T extends Record<string, unknown> | undefined = undefined> extends UniqueInterface {
	status: number;
	data: T;
}

image
image

@Rich-Harris Rich-Harris merged commit 2776f33 into sveltejs:master Sep 21, 2022
@Rich-Harris
Copy link
Member

thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants