kit icon indicating copy to clipboard operation
kit copied to clipboard

Missing property in type from ActionData

Open harrylaulau opened this issue 3 years ago • 5 comments

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: {} });
	}
};

CleanShot 2022-09-23 at 23 33 43

+page.svelte CleanShot 2022-09-23 at 23 35 27

  1. return type with something does not exist
  2. return type with common2 having 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

harrylaulau avatar Sep 23 '22 22:09 harrylaulau

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: {} });
	}
};
```

dummdidumm avatar Sep 24 '22 18:09 dummdidumm

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

madupuis90 avatar Feb 02 '23 00:02 madupuis90

The only workaround is to manually type the return type

dummdidumm avatar Feb 02 '23 04:02 dummdidumm

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.

f15u avatar Jul 14 '23 14:07 f15u

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 image

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. image

Also I had to use fail instead of invalid since there were a breaking change 😒

kno-raziel avatar Aug 25 '23 19:08 kno-raziel