13

I see that this answer suggests the syntax for importing images as shown below (commented out). In my case, it didn't work out (complaining there's no modules to find in that file) and I had to switch to the syntax that's currently active.

// import Author from "../assets/author.png";
var Author = require("../assets/author.png");

The difference I can imagine is that I'm using TypeScript (transpiling my TSX by awesome-typescript-loader and loading my PNG file-loader) and they seem to use JSX. But as far my understanding goes, it all transpiles to plain JS and require in the end.

Being a noob on React, I'm not sure what the reason of this discrepancy is but also I'm not sure what to google for to investigate myself.

Aruna Herath
  • 6,241
  • 1
  • 40
  • 59
DonkeyBanana
  • 3,266
  • 4
  • 26
  • 65

4 Answers4

5

This is more of a problem with typescript than webpack itself, you might need to declare modules on a declaration file.

Create a declarations.d.ts

Update your tsconfig.json

"include": [
    "./declarations.d.ts",
],

Put this on that file:

declare module '*.png';

Error might be gone.

PlayMa256
  • 6,603
  • 2
  • 34
  • 54
  • Nope, I did just as you suggested and the error remains. It still complains about not being able to locate the module and the image isn't rendered. – DonkeyBanana Sep 18 '18 at 16:52
  • well, that goes out of the scope of what i know from ts. Can you share the entire error message? – PlayMa256 Sep 18 '18 at 17:00
  • *ERROR in [at-loader] ./src/components/MainArea.tsx:3:21* and then *TS2307: Cannot find module '../assets/author.png'.* – DonkeyBanana Sep 19 '18 at 12:31
1

You can declare a module for your images like this:

declare module "*.png" {
  const value: any;
  export default value;
}

Then, you will be able to import your image like this:

import AuthorSrc from "../assets/author.png";

This is happening because webpack doesn't support image import out of the box. So you need to add a rule for that in the webpack config file. When you add a new rule, TypeScript doesn't automatically know that, so you need to declare a new module to resolve this. Without the module, you will be able to import images, but TypeScript will throw an error because you didn't tell to it is possible.

Vincent D'amour
  • 3,746
  • 1
  • 27
  • 39
  • It seems to have removed the error **but** I don't get to see the image, just a border around it, while the background is the same as the rest of the page. I'm using the following rule for loading the image: *{test: /\.(png)$/,use: [{ loader: "file-loader" }]}*. Can you imagine a more suited one? – DonkeyBanana Sep 28 '18 at 10:17
  • Also, I'm curious if the *const value: any; export default value;* is equivalent to simply setting a semicolon instead of the braces. I saw people declare the module like this: *declare module "bzz";* and it unclear to me if it's a shorthand for *declare module "bzz"{...}*. – DonkeyBanana Sep 28 '18 at 10:19
1

This issue has nothing to do with webpack or any bundler and is not quite a problem with typescript.

Typescript has stated that `require("path") is a way to include modules to the scope of your current module, whilst it can be also used to read some random files (such as json files, for example).

As Vincent and Playma256 specified, you can declare a module wildcard to match certain file types, so you can import it as an import statement. But you don't really need to do this. Typescript won't give you an error if you are trying to import a png or a json file (tslint might, but that depends on your configuration).

By the way, if your declaration is within the source folder of your project as defined in tsconfig.json, you don't need to include it as specified by Playma256.

I've created a sample project in node for you to test:

https://github.com/rodrigoelp/typescript-declare-files

rodrigoelp
  • 2,550
  • 1
  • 19
  • 29
  • I'm not entirely sure it's right on. If I get it properly, you import a *JSON* file not an image and that actually might make a difference because of what kind of loader you're using, doesn't it? I'm using *file-loader* for the *PNG* of mine and I'm getting as far as to see a border around the spot where the image's supposed to be (but no actual image). Did you actually get to **see** the image being rendered? (The error message vanishes, so the answer's kind of correct - but I'd like to see the author too, hehe.) – DonkeyBanana Sep 28 '18 at 10:24
  • Also, I've seen both *declare module "*.xxx";* and *declare module "*.xxx"{const v:any;return default v}*. Are those equivalent? – DonkeyBanana Sep 28 '18 at 10:26
  • Hi @DonkeyBanana, trying to give you a bit of context: "what kind of loader I am using?" if you meant the bundler, I tested this directly with node, but it was tested as well with webpack and metro. "I am importing a json file as opposed to a png", the compilation done by typescript is going to be the same. Any import statement is transpiled to a require, AFAIK. Shouldn't make any difference. When I did my little test, I did get to see the image, but my test was done using react (which I think is the same thing you are doing) – rodrigoelp Sep 29 '18 at 12:57
  • In regards to: Is `declare module "*.xxx";` equivalent to `declare module "*.xxx" { const v: any; export default v }`? No, the first one is just declaring the existence of a module by that name, the second one tells typescript that it needs to capture by default the instance or definition that has been exported. – rodrigoelp Sep 29 '18 at 13:00
  • Please apologize my ignorance when formulating the question. I didn't mean the type of bundler (I'm always using Webpack for that BTW). However, when one configures it, there's a section for rules and in that, I had to specify *awesome-typescript-loader* for TS, *style-loader*, *css-loader* and *sass-loader* for SCSS but also *file-loader*, which is supposedly be required for the static files (i.e. my PNG). What I'm asking is whether *file-loader* is the one preferred by config of Webpack to render PNG images on the page. – DonkeyBanana Sep 29 '18 at 13:43
  • Ah, I understand now. Yes, `file-loader` is one of the most popular. if you try to import the image by calling `import fileName from "./whatever.png"` be sure you have the proper configuration (such as publicPath or the regular expression searching for the files you want to locate), otherwise the import is going to give you `undefined` which basically means "not found" and you will end up with the scenario stated above. – rodrigoelp Oct 01 '18 at 11:51
  • One way you can test this is, just call `require("./fileName.png")` and configure your webpack.config.js with the `publicPath` to the assets folder (where you stored all the images). If you console log it, you should be able to see if there was an undefined instance or the actual image. Alternatively, configure vscode to run each line (you need to compile with sourceMaps) and check the instance isn't `undefined` – rodrigoelp Oct 01 '18 at 11:53
1

I think you can solve this problem with Webpack&&typescript.The official webpage of webpack has introduced something about this in https://webpack.js.org/guides/typescript/ And I have try this myself in https://github.com/reactpersopnal/webpack-root/tree/feature/typescript The reason is that you would like to use non-code assets with TypeScript, so we need to defer the type for these imports for webpack. Your could simply add custom.d.ts.

declare module "*.jpg" {
    const content: any;
    export default content;
}
Root
  • 2,301
  • 1
  • 10
  • 14