kit
kit copied to clipboard
Missing property in type from ActionData
Describe the bug
This is to reopen issue #6823
First of all, thanks everyone for helping with this issue, but even after upgrading to the newest sveltekit version, the bug still persists. I have updated the example repo for clearer demonstration of the bug: https://github.com/harrylaulau/sveltekittest
First issue is that when one property exist in one invalid() but not another, the type of return object with that property does not exist.
Second issue is when a property is of object type, it seems to override all other type
+page.server.ts
import { invalid, redirect } from '@sveltejs/kit';
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({ request, url }) => {
if (url.searchParams.get('v') === '0') return invalid(400, { common: '1' });
if (url.searchParams.get('v') === '1') return invalid(400, { common: '2', something: '' });
if (url.searchParams.get('v') === '2') return invalid(400, { common2: 1 });
if (url.searchParams.get('v') === '3') return invalid(400, { common2: '1' });
if (url.searchParams.get('v') === '4') return invalid(400, { common2: {} });
}
};

+page.svelte

- return type with
somethingdoes not exist - return type with
common2having the string type or a number type does not exist
Reproduction
https://github.com/harrylaulau/sveltekittest
Logs
No response
System Info
System:
OS: macOS 12.6
CPU: (10) arm64 Apple M1 Max
Memory: 7.60 GB / 32.00 GB
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 16.15.0 - ~/.nvm/versions/node/v16.15.0/bin/node
Yarn: 1.22.19 - ~/Library/pnpm/yarn
npm: 8.12.1 - ~/.nvm/versions/node/v16.15.0/bin/npm
Watchman: 2022.09.12.00 - /opt/homebrew/bin/watchman
Browsers:
Chrome: 105.0.5195.125
Safari: 16.0
npmPackages:
@sveltejs/adapter-auto: next => 1.0.0-next.80
@sveltejs/kit: next => 1.0.0-next.502
svelte: ^3.44.0 => 3.50.1
vite: ^3.1.0 => 3.1.3
Severity
serious, but I can work around it
Additional Information
No response
This looks like an upstream TypeScript issue. Of you hover over the default property in +page.server.ts, you can see that TypeScript infers the type as this:
(property) default: ({ request, url }: RequestEvent<RouteParams>) => Promise<ValidationError<{
common: string;
}> | ValidationError<{
common2: {};
}>>
... which already doesn't include the things you want to. So there's nothing we can do about this. I think in this case you need to type the return type explicitly yourself:
import { invalid, type ValidationError } from '@sveltejs/kit';
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({ url }): Promise<ValidationError<{common: string} | {common: string; something: string} | {common2: string | number | {}}>> => {
if (url.searchParams.get('v') === '0') return invalid(400, { common: '1' });
if (url.searchParams.get('v') === '1') return invalid(400, { common: '2', something: '' });
if (url.searchParams.get('v') === '2') return invalid(400, { common2: 1 });
if (url.searchParams.get('v') === '3') return invalid(400, { common2: '1' });
if (url.searchParams.get('v') === '4') return invalid(400, { common2: {} });
}
};
```
I'm having the same issue. My +page.server.ts for login/register will return either a formData validation error (from zod), a bad credentials error (from failing to auth) or throw a redirect in the case of success. My +page.svelte is not able to assert the proper type, I tried tweaking the generated types a couple times, write very long guards or split the code in multiple pages, but I ended up just typing FormAction with any. Not my preferred way of solving this, but oh well. I'm not quite sure I understand what the problem is upsteam, but I also wouldn't mind a workaround.
edit: I think my was issue was unrelated and has been fixed: https://github.com/sveltejs/kit/issues/8865
The only workaround is to manually type the return type
The only workaround is to manually type the return type
Hello! Can you show an example, even in pseudo code, on how and where to manually type the return type? I did try some manual typing without success.
I was having same experience while following a Supabase & Sveltekit tutorial...
+page.server.ts
import { AuthApiError } from '@supabase/supabase-js';
import { fail } from '@sveltejs/kit';
export const actions = {
async default({ request, url, locals: { supabase } }) {
const formData = await request.formData();
const email = formData.get('email') as string;
const password = formData.get('password') as string;
if (!email) {
return fail(400, {
error: 'Please enter your email',
});
}
if (!password) {
return fail(400, {
error: 'Please enter a password',
values: {
email,
},
});
}
const { error } = await supabase.auth.signUp({
email,
password,
options: { emailRedirectTo: url.origin },
});
if (error) {
if (error instanceof AuthApiError && error.status === 400) {
return fail(400, {
error: 'Invalid credentials.',
values: {
email,
},
});
}
return fail(500, {
error: 'Server error. Try again later.',
values: {
email,
},
});
}
return {
message: 'Please check your email for a magic link to log into the website.',
};
},
};
+page.svelte
the type returned by ActionData wasn’t able to infer the right type from all the posible returns within the action.
I followed the suggestion from @dummdidumm and typed the return from the action this way 🫤
import { AuthApiError } from '@supabase/supabase-js';
import { ActionFailure, fail } from '@sveltejs/kit';
type DefaultAction = Promise<
| ActionFailure<{
error: string;
values?: {
email: string;
};
}>
| { message: string }
>;
export const actions = {
async default({ request, url, locals: { supabase } }): DefaultAction {
const formData = await request.formData();
const email = formData.get('email') as string;
const password = formData.get('password') as string;
if (!email) {
return fail(400, {
error: 'Please enter your email',
});
}
if (!password) {
return fail(400, {
error: 'Please enter a password',
values: {
email,
},
});
}
const { error } = await supabase.auth.signUp({
email,
password,
options: { emailRedirectTo: url.origin },
});
if (error) {
if (error instanceof AuthApiError && error.status === 400) {
return fail(400, {
error: 'Invalid credentials.',
values: {
email,
},
});
}
return fail(500, {
error: 'Server error. Try again later.',
values: {
email,
},
});
}
return {
message: 'Please check your email for a magic link to log into the website.',
};
},
};
and as we can see the type was the expected and the complaint about the type for form?.values?.email was gone.
Also I had to use fail instead of invalid since there were a breaking change 😒