9

Next.js has recently made a modification (in v11.0.x) which has the following type definitions:

In next-env.d.ts (non-modifiable, regenerated at every build):

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

In node_modules/next/image-types/global.d.ts (non-modifiable, don't wanna use patch-package):

declare module '*.svg' {
  const content: any
  export default content
}

Now the issue is that I am using @svgr/webpack, and as a result I need to do the following:

declare module '*.svg' {
  const content: React.FC<React.SVGAttributes<SVGElement>>
  export default content
}

Earlier placing this code in index.d.ts in the assets folder used to work. But now it doesn't and as result I am forced to cast every import separately. Any way to do this directly?

brc-dd
  • 10,788
  • 3
  • 47
  • 67

2 Answers2

9

I am using the following workaround:

  • Add next-env.d.ts to exclude array in tsconfig.json:

    {
      // ...
      "exclude": ["node_modules", "next-env.d.ts"]
    }
    
  • Add next-env.d.ts to .gitignore/.eslintignore.

  • Create new file custom.d.ts:

    /// <reference types="next" />
    /// <reference types="next/types/global" />
    
    // additional things that one used to put here before Next.js v11
    
  • Create new file images.d.ts:

    type StaticImageData = {
      src: string;
      height: number;
      width: number;
      placeholder?: string;
    };
    
    declare module '*.png' {
      const content: StaticImageData;
      export default content;
    }
    
    declare module '*.svg' {
      const content: React.FC<React.SVGProps<SVGSVGElement>>;
      export default content;
    }
    
    declare module '*.jpg' {
      const content: StaticImageData;
      export default content;
    }
    
    declare module '*.jpeg' {
      const content: StaticImageData;
      export default content;
    }
    
    declare module '*.gif' {
      const content: StaticImageData;
      export default content;
    }
    
    declare module '*.webp' {
      const content: StaticImageData;
      export default content;
    }
    
    declare module '*.ico' {
      const content: StaticImageData;
      export default content;
    }
    
    declare module '*.bmp' {
      const content: StaticImageData;
      export default content;
    }
    
  • Make sure that these files are handled by patterns specified in include array of tsconfig.

  • Add declarations for *.avif too if you're using them in next@12:

    declare module '*.avif' {
      const content: StaticImageData
      export default content
    }
    
brc-dd
  • 10,788
  • 3
  • 47
  • 67
  • 1
    just wanted to say it works like a charm, way to go man, really clever solution, love it – Божидар Йовчев Dec 06 '21 at 16:33
  • 1
    Instead of excluding `next-env.d.ts` from `tsconfig.json`, [disabling static image import](https://nextjs.org/docs/api-reference/next/image#disable-static-imports) disables the type declarations for images. In Next.js source code, [image-types/global.d.ts](https://github.com/vercel/next.js/blob/canary/packages/next/image-types/global.d.ts) says `// this file is conditionally added/removed to next-env.d.ts // if the static image import handling is enabled` – Tomoyuki Aota Jan 26 '22 at 15:02
  • @TomoyukiAota That will disable importing non-svg images (i.e., png, jpg, etc.) too. – brc-dd Jan 26 '22 at 15:10
6

An alternative to replacing next-env.d.ts is to add an additional declarations file, e.g. additional.d.ts, as described in https://nextjs.org/docs/basic-features/typescript.

As noted in the docs, this file must be included in the include array of your tsconfig.json file, but I've found the order is important as well - the additional file must be included before next-env.d.ts.

This seems to be because when there are multiple declarations for the same module containing default exports, the first declaration wins.

// additional.d.ts
declare module "*.svg" {
  import React from "react";

  const content: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;

  export default content;
}
// tsconfig.json
{
  "include": ["additional.d.ts", "next-env.d.ts"] // The order matters!
}

This gives our preferred type for SVGs, whilst preserving the other desirable behaviour from next-env.d.ts.

This seems preferable to simply replacing the built-in types file as the Next team have reserved the right to adjust the types in future, meaning the copied types would be need to manually be kept up-to-date, and this approach doesn't seem to be a documented approach.

Laurence
  • 419
  • 1
  • 5
  • 12