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

usePage types not inheriting @inertiajs/core Page type #1381

Closed
nurdism opened this issue Jan 14, 2023 · 15 comments · Fixed by #1394
Closed

usePage types not inheriting @inertiajs/core Page type #1381

nurdism opened this issue Jan 14, 2023 · 15 comments · Fixed by #1394
Assignees

Comments

@nurdism
Copy link

nurdism commented Jan 14, 2023

Version:

  • @inertiajs/vue3 version: 1.0.0

Describe the problem:

@inertiajs/vue3/types/app.d.ts@usePage types are declared as

export declare function usePage(): {
    component?: string;
    props?: {
        [x: string]: unknown;
        errors: import("@inertiajs/core").Errors & import("@inertiajs/core").ErrorBag;
    };
    url?: string;
    version?: string;
    scrollRegions?: {
        top: number;
        left: number;
    }[];
    rememberedState?: Record<string, unknown>;
    resolvedErrors?: import("@inertiajs/core").Errors;
};

it does not inherit from the Page type from @inertiajs/core and it causing all instances of usePage() to ignore the PageProps interface in @inertiajs/core/types/types.ts

if extending the PageProps interface via

declare module '@inertiajs/core' {
  interface PageProps {
    myProp: string
  }
}

it does not reflect in usePage() typings

@travis-r6s
Copy link

travis-r6s commented Jan 14, 2023

I managed to override this by re-declaring + re-exporting the whole function, as it isn't an interface:

import { router } from '@inertiajs/vue3'

declare module '@inertiajs/vue3' {
  export declare const router: router

  export declare function usePage<Props> (): {
    component: string;
    props: Props & {
      errors?: import('@inertiajs/core').Errors & import('@inertiajs/core').ErrorBag;
    };
    url: string;
    version?: string;
    scrollRegions?: {
      top: number;
      left: number;
    }[];
    rememberedState?: Record<string, unknown>;
    resolvedErrors?: import('@inertiajs/core').Errors;
  }
}

Just out of interest, was there a particular reason the generic type argument was removed from the usePage function? I used to be able to type the props with usePage<SomePropsInterface>, but now I need to use page.props as unknown as SomePropsInterface, which doesn't look particularly neat...

Similarly, the useForm extends Record<string, unknown>, which causes some type errors if I have an interface with optional keys for example... I managed to fix this by re-exporting those functions too:

function useForm<TForm = Record<string, unknown>> (data: TForm): InertiaForm<TForm>
export default function useForm<TForm = Record<string, unknown>> (rememberKey: string, data: TForm): InertiaForm<TForm>

@nurdism
Copy link
Author

nurdism commented Jan 14, 2023

I have also come up with a solution to this by re-exporting, like so:
globals.d.ts

import type { Page } from '@inertiajs/core'
declare module '@inertiajs/vue3' {
  export declare function usePage<T>(): Page<T>
}

the Page type accepts a generic

@travis-r6s
Copy link

Ah, perfect, that's a much nicer solution! Thanks @nurdism

@WalrusSoup
Copy link

export declare function usePage(): Page

I'm not sure why, but this was throwing an error for me. I adjusted it and and it seems to be ok with this instead?

import type { Page } from '@inertiajs/core';
declare module '@inertiajs/vue3' {
    export function usePage<T>(): Page<T>
}

@johan-paqt
Copy link

johan-paqt commented Jan 16, 2023

export declare function usePage(): Page

I'm not sure why, but this was throwing an error for me. I adjusted it and and it seems to be ok with this instead?

import type { Page } from '@inertiajs/core';
declare module '@inertiajs/vue3' {
    export function usePage<T>(): Page<T>
}

This worked like a charm for me, and I combine it with my own definition for the props, like so:

import type { Page, PageProps } from '@inertiajs/core';

declare module '@inertiajs/vue3' {
    export function usePage<T>(): Page<T>;
}

declare global {
    export interface InertiaProps extends Page<PageProps> {
        flashNotifications: Array<App.FlashNotification>;
    }
}

And then you can use it like so:

const notifications = computed(() => usePage<InertiaProps>().props.flashNotifications);

And get full autocompletion for props.

I'm not a typescript expert, maybe there is a better way of doing this, please let me know, but it works like a charm.

@nurdism
Copy link
Author

nurdism commented Jan 16, 2023

declare module '@inertiajs/core' {
  interface PageProps { // you can define global $page/usePage() props by extending/defining this interface 
    myGlobalProp: string[]
  }
}

declare module '@inertiajs/vue3' {
  export function usePage<T>(): Page<T> // the T generic will combine any type you add to it & the PageProps interface defined in @inertiajs/core
}

const page = usePage<{ myLocalProp: string }>() // the types for page.props should be { myGlobalProp: string[]; myLocalProp: string }
const page = usePage() // the types for page.props should be { myGlobalProp: string[]; }

@johan-paqt
Copy link

declare module '@inertiajs/vue3' {
  export function usePage<T>(): Page<T> // the T generic will combine any type you add to it & the PageProps interface defined in @inertiajs/core
}

You just blew my mind, thank you! Much, much cleaner.

@WalrusSoup
Copy link

This worked like a charm for me, and I combine it with my own definition for the props, like so:

I use it slightly differently than your example. Seems to work fine:

import type { Page } from '@inertiajs/core';

declare module '@inertiajs/vue3' {
    export function usePage<T>(): Page<T>
}


interface GlobalProps extends Page {
    meta: {
        user: LoggedInUser
    },
    flash: {
        message: string | null,
        error: string | null
    }
}

interface ManageUserPageProps extends GlobalProps {
    user: FullUser
    available_roles: Role[]
    available_permissions:  string[]
}

I shove all my page prop definitions into that file. Then, in my vue files:

const available_roles = usePage<ManageUserPageProps>().props.available_roles;

Seems to work fine.

@jessarcher
Copy link
Member

Just out of interest, was there a particular reason the generic type argument was removed from the usePage function? I used to be able to type the props with usePage<SomePropsInterface>, but now I need to use page.props as unknown as SomePropsInterface, which doesn't look particularly neat...

I forgot to copy that definition from the old type definitions when converting the adapters to TypeScript, so they were just inferred without the generic.

I've created #1394 that attempts to address this; however, I can't currently get it to build.

@feras1984
Copy link

feras1984 commented Oct 27, 2023

import type { Page } from '@inertiajs/core';
declare module '@inertiajs/vue3' {
export function usePage<T extends PageProps>() : Page<T>
}

While T accepts a generic type, Page accepts PageProps interface. So, we need to add T extends PageProps


@Smef
Copy link
Contributor

Smef commented Feb 11, 2024

I've been trying to work with typing usePage() using PNPM. Has anyone had any luck with that? With PNPM you can't import types from @inertiajs/core since it's a child dependency of @inertiajs/vue3 and not a direct project-level dependency.

Separately installing @inertiajs/core as a project dependency would be possible, but it seems like a bad idea as you'd need to separately manage the versions of these two packages instead of just letting @inertiajs/vue3 keep track of the versions on its own.

Is there a way to type usePage() without also adding @inertiajs/core as a project-level dependency?

Here's what works great if you can import from @inertiajs/core (which works fine with NPM). This updated the definition of usePage everywhere so you don't have to manually type it each time:

SharedProps.d.ts

import type User from "@/Types/Models/User";
import type { Page, PageProps } from "@inertiajs/core";

interface SharedProps extends PageProps {
    auth: {
        user: User;
    };
}

export default SharedProps;

declare module "@inertiajs/vue3" {
    export function usePage(): Page<SharedProps>;
}

The solution below sort of works with PNPM, but it less convenient because you have to manually type every instance of usePage. The [key: string]: any bit also seems like it isn't a good idea, but seems to be necessary:

SharedProps.d.ts

import type User from "@/Types/Models/User";

interface SharedProps{
    auth: {
        user: User;
    };
    [key: string]: any;
}

export default SharedProps;
const page = usePage<SharedProps>();

@rikarani
Copy link

import type User from "@/Types/Models/User";
import type { Page, PageProps } from "@inertiajs/core"; 
interface SharedProps extends PageProps {
     auth: {
         user: User;
     };
 }
 
export default SharedProps;
 
declare module "@inertiajs/vue3" {
     export function usePage(): Page<SharedProps>;
}

thanks @Smef, it works on me

@melvinotieno
Copy link

melvinotieno commented May 1, 2024

I've been trying to work with typing usePage() using PNPM. Has anyone had any luck with that? With PNPM you can't import types from @inertiajs/core since it's a child dependency of @inertiajs/vue3 and not a direct project-level dependency.

Separately installing @inertiajs/core as a project dependency would be possible, but it seems like a bad idea as you'd need to separately manage the versions of these two packages instead of just letting @inertiajs/vue3 keep track of the versions on its own.

Is there a way to type usePage() without also adding @inertiajs/core as a project-level dependency?

@Smef @rikarani

Solution for when working with pnpm that I figured out

  1. Create .npmrc
  2. Add the following line public-hoist-pattern[]=@inertiajs/core
  3. Do a clean install pnpm install

Reference https://pnpm.io/npmrc#public-hoist-pattern

@saintpickle
Copy link

I had this issue with the Laravel 11 Breeze React Starter Kit and I had to put this in the global.d.ts file.

declare module "@inertiajs/react" {
    export declare function usePage<T>(): Page<T>;
}

@francoism90
Copy link

I'm using something like this on Laravel 11 and Inertia V2:

import type { Page } from '@inertiajs/core'

declare module '@inertiajs/core' {
  interface PageProps {
    auth: {
      logout: {
        route: string
      }
    }
  }
}

declare module '@inertiajs/vue3' {
  export declare function usePage<T>(): Page<T>
}

I saved it as resources/js/types/interia.d.ts.

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 a pull request may close this issue.