【问题标题】:extending nextJS default types with typescript使用 typescript 扩展 nextJS 默认类型
【发布时间】:2021-06-04 11:23:45
【问题描述】:

目前我正在这样做

router.d.ts

import { useRouter } from 'next/router'

declare global {
  type TRouter = ReturnType<typeof useRouter> & {
    query: {
      ticketNumber: string
    }
  }
}

并像这样使用它:

const { query } = useRouter() as TRouter

我试图避免 as 并使用我的自定义类型扩展 nextJS 类型是

next-env.d.ts

/// <reference types="next" />
/// <reference types="next/types/global" />

import * as router from 'next/router'

declare module 'next/router' {
  function useRouter(): ReturnType<typeof router.useRouter> & {
    query: {
      ticketNumber: string
    }
  }

  export { useRouter }
}

但这似乎完全破坏了模块next/router..的类型。

【问题讨论】:

    标签: javascript typescript types


    【解决方案1】:

    对于模块扩充,您需要扩展默认类型 useRouter() 并吸收您的自定义集成。

    useRouter的定义

    /// <reference types="node" />
    import React from 'react';
    import Router, { NextRouter } from '../next-server/lib/router/router';
    declare type SingletonRouterBase = {
        router: Router | null;
        readyCallbacks: Array<() => any>;
        ready(cb: () => any): void;
    };
    export { Router, NextRouter };
    export declare type SingletonRouter = SingletonRouterBase & NextRouter;
    declare const _default: SingletonRouter;
    export default _default;
    export { default as withRouter } from './with-router';
    export declare function useRouter(): NextRouter;
    export declare const createRouter: (pathname: string, query: import("querystring").ParsedUrlQuery, as: string, __3: {
        subscription: (data: import("../next-server/lib/router/router").PrivateRouteInfo, App: React.ComponentType<import("../next-server/lib/router/router").AppProps>, resetScroll: {
            x: number;
            y: number;
        } | null) => Promise<void>;
        initialProps: any;
        pageLoader: any;
        Component: React.ComponentType<{}>;
        App: React.ComponentType<import("../next-server/lib/router/router").AppProps>;
        wrapApp: (WrapAppComponent: React.ComponentType<import("../next-server/lib/router/router").AppProps>) => any;
        err?: Error | undefined;
        isFallback: boolean;
        locale?: string | undefined;
        locales?: string[] | undefined;
        defaultLocale?: string | undefined;
        domainLocales?: import("../next-server/server/config-shared").DomainLocales | undefined;
        isPreview?: boolean | undefined;
    }) => Router;
    export declare function makePublicRouterInstance(router: Router): NextRouter;
    
    

    useRouter 的类型为 function useRouter(): NextRouter

    NextRouter 定义为

    type NextRouter = BaseRouter & Pick<Router, "push" | "replace" | "reload" | "back" | "prefetch" | "beforePopState" | "events" | "isFallback" | "isReady" | "isPreview">
    

    所以,你可以扩展BaseRouter

    type BaseRouter = {
        route: string;
        pathname: string;
        query: ParsedUrlQuery;
        asPath: string;
        basePath: string;
        locale?: string;
        locales?: string[];
        defaultLocale?: string;
        domainLocales?: DomainLocales;
        isLocaleDomain: boolean;
    }
    

    在你调用 useRouter() 时定义

    您可以扩展类 Router,实现 BaseRouter,这将允许您有条件地选择您正在合并的类型,同时不损害默认路由器的完整性

    export default class Router implements BaseRouter {
        route: string;
        pathname: string;
        query: ParsedUrlQuery;
        asPath: string;
        basePath: string;
        /**
         * Map of all components loaded in `Router`
         */
        components: {
            [pathname: string]: PrivateRouteInfo;
        };
        sdc: {
            [asPath: string]: object;
        };
        sdr: {
            [asPath: string]: Promise<object>;
        };
        sub: Subscription;
        clc: ComponentLoadCancel;
        pageLoader: any;
        _bps: BeforePopStateCallback | undefined;
        events: MittEmitter;
        _wrapApp: (App: AppComponent) => any;
        isSsr: boolean;
        isFallback: boolean;
        _inFlightRoute?: string;
        _shallow?: boolean;
        locale?: string;
        locales?: string[];
        defaultLocale?: string;
        domainLocales?: DomainLocales;
        isReady: boolean;
        isPreview: boolean;
        isLocaleDomain: boolean;
        private _idx;
        static events: MittEmitter;
        constructor(pathname: string, query: ParsedUrlQuery, as: string, { initialProps, pageLoader, App, wrapApp, Component, err, subscription, isFallback, locale, locales, defaultLocale, domainLocales, isPreview, }: {
            subscription: Subscription;
            initialProps: any;
            pageLoader: any;
            Component: ComponentType;
            App: AppComponent;
            wrapApp: (WrapAppComponent: AppComponent) => any;
            err?: Error;
            isFallback: boolean;
            locale?: string;
            locales?: string[];
            defaultLocale?: string;
            domainLocales?: DomainLocales;
            isPreview?: boolean;
        });
        onPopState: (e: PopStateEvent) => void;
        reload(): void;
        /**
         * Go back in history
         */
        back(): void;
        /**
         * Performs a `pushState` with arguments
         * @param url of the route
         * @param as masks `url` for the browser
         * @param options object you can define `shallow` and other options
         */
        push(url: Url, as?: Url, options?: TransitionOptions): Promise<boolean>;
        /**
         * Performs a `replaceState` with arguments
         * @param url of the route
         * @param as masks `url` for the browser
         * @param options object you can define `shallow` and other options
         */
        replace(url: Url, as?: Url, options?: TransitionOptions): Promise<boolean>;
        private change;
        changeState(method: HistoryMethod, url: string, as: string, options?: TransitionOptions): void;
        handleRouteInfoError(err: Error & {
            code: any;
            cancelled: boolean;
        }, pathname: string, query: ParsedUrlQuery, as: string, routeProps: RouteProperties, loadErrorFail?: boolean): Promise<CompletePrivateRouteInfo>;
        getRouteInfo(route: string, pathname: string, query: any, as: string, resolvedAs: string, routeProps: RouteProperties): Promise<PrivateRouteInfo>;
        set(route: string, pathname: string, query: ParsedUrlQuery, as: string, data: PrivateRouteInfo, resetScroll: {
            x: number;
            y: number;
        } | null): Promise<void>;
        /**
         * Callback to execute before replacing router state
         * @param cb callback to be executed
         */
        beforePopState(cb: BeforePopStateCallback): void;
        onlyAHashChange(as: string): boolean;
        scrollToHash(as: string): void;
        urlIsNew(asPath: string): boolean;
        /**
         * Prefetch page code, you may wait for the data during page rendering.
         * This feature only works in production!
         * @param url the href of prefetched page
         * @param asPath the as path of the prefetched page
         */
        prefetch(url: string, asPath?: string, options?: PrefetchOptions): Promise<void>;
        fetchComponent(route: string): Promise<GoodPageCache>;
        _getData<T>(fn: () => Promise<T>): Promise<T>;
        _getStaticData(dataHref: string): Promise<object>;
        _getServerData(dataHref: string): Promise<object>;
        getInitialProps(Component: ComponentType, ctx: NextPageContext): Promise<any>;
        abortComponentLoad(as: string, routeProps: RouteProperties): void;
        notify(data: PrivateRouteInfo, resetScroll: {
            x: number;
            y: number;
        } | null): Promise<void>;
    }
    

    例如,我在此处扩展了 AppProps 以合并一个 Session 对象,以便我可以调用 pageProps.session 以将用户会话对象注入 NextAuth

    next.d.ts

    import type { NextComponentType, NextPageContext } from 'next';
    import type { Session } from 'next-auth';
    import type { Router } from 'next/router';
    declare module 'next/app' {
        type AppProps<P = Record<string, unknown>> = {
            Component: NextComponentType<NextPageContext, any, P>;
            router: Router;
            __N_SSG?: boolean;
            __N_SSP?: boolean;
            pageProps: P & {
                /** Initial session passed in from `getServerSideProps` or `getInitialProps` */
                session?: Session;
            };
        };
    }
    
    

    【讨论】:

    • 非常感谢您抽出这么多时间来帮助我。我对打字稿很陌生。您能否详细说明我如何扩展现有的“全局”BaseRouter - 似乎无法弄清楚如何导入和扩展它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-16
    • 2017-06-28
    • 2021-01-18
    相关资源
    最近更新 更多