无法导入CSS/SCSS模块。TypeScript显示“找不到模块”。

239
我正在尝试从CSS模块中导入一个主题,但是TypeScript给了我一个“找不到模块”的错误,并且主题在运行时没有应用。我认为我的Webpack配置有问题,但我不确定问题出在哪里。
我正在使用以下工具:
"typescript": "^2.0.3"
"webpack": "2.1.0-beta.25"
"webpack-dev-server": "^2.1.0-beta.9"
"react": "^15.4.0-rc.4"
"react-toolbox": "^1.2.3"
"node-sass": "^3.10.1"
"style-loader": "^0.13.1"
"css-loader": "^0.25.0"
"sass-loader": "^4.0.2"
"sass-lint": "^1.9.1"
"sasslint-webpack-plugin": "^1.0.4"

这是我的webpack.config.js文件。
var path = require('path');
var webpack = require('webpack');
var sassLintPlugin = require('sasslint-webpack-plugin');

module.exports = {
  entry: [
    'webpack-dev-server/client?http://localhost:8080',
    'webpack/hot/dev-server',
    './src/index.tsx',
  ],
  output: {
    path: path.resolve(__dirname, 'dist'),
    publicPath: 'http://localhost:8080/',
    filename: 'dist/bundle.js',
  },
  devtool: 'source-map',
  resolve: {
    extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js'],
  },
  module: {
    rules: [{
      test: /\.js$/,
      loader: 'source-map-loader',
      exclude: /node_modules/,
      enforce: 'pre',
    }, {
      test: /\.tsx?$/,
      loader: 'tslint-loader',
      exclude: /node_modules/,
      enforce: 'pre',
    }, {
      test: /\.tsx?$/,
      loaders: [
        'react-hot-loader/webpack',
        'awesome-typescript-loader',
      ],
      exclude: /node_modules/,
    }, {
      test: /\.scss$/,
      loaders: ['style', 'css', 'sass']
    }, {
      test: /\.css$/,
      loaders: ['style', 'css']
    }],
  },
  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM'
  },
  plugins: [
    new sassLintPlugin({
      glob: 'src/**/*.s?(a|c)ss',
      ignoreFiles: ['src/normalize.scss'],
      failOnWarning: false, // Do it.
    }),
    new webpack.HotModuleReplacementPlugin(),
  ],
  devServer: {
    contentBase: './'
  },
};

我有一个 App.tsx 文件,我试图导入以下内容:

import * as React from 'react';

import { AppBar } from 'react-toolbox';
import appBarTheme from 'react-toolbox/components/app_bar/theme.scss'
// local ./theme.scss stylesheets aren't found either 

interface IAppStateProps {
  // No props yet
}

interface IAppDispatchProps {
  // No state yet
}

class App extends React.Component<IAppStateProps & IAppDispatchProps, any> {

  constructor(props: IAppStateProps & IAppDispatchProps) {
    super(props);
  }

  public render() {
    return (

        <div className='wrapper'>
          <AppBar title='My App Bar' theme={appBarTheme}>
          </AppBar>
        </div>

    );
  }
}

export default App;

需要什么条件才能实现类型安全的样式表模块导入?
23个回答

276

TypeScript不知道除了 .ts.tsx 之外还有其他类型的文件,因此如果导入具有未知文件后缀名的文件时,它会抛出错误。

如果您有一个 webpack 配置文件,允许您导入其他类型的文件,那么您必须告诉 TypeScript 编译器这些文件的存在。为此,需要添加一个声明文件,在其中声明具有适当名称的模块。

要声明的模块内容取决于用于文件类型的 webpack 加载器。在将 *.scss 文件通过 sass-loadercss-loaderstyle-loader 管道处理的 webpack 配置中,导入的模块中不会有内容,并且正确的模块声明应如下所示:

// declaration.d.ts
declare module '*.scss';

如果加载器已经配置为css-modules,则可以像这样扩展声明:

如果加载器已配置好css-modules,只需按如下方式扩展声明:
// declaration.d.ts
declare module '*.scss' {
    const content: Record<string, string>;
    export default content;
}

7
嗨,这对我没有用,最近有什么变化吗?如何将 SCSS 文件导入 TypeScript 文件? - Kay
2
供可能需要的任何人参考。 对我来说,我必须将declaration.d.ts文件放在项目根目录中。将其放在tsconfig文件中“types”下路径的子文件夹中对我没有起作用。 - Latchy
我曾使用typescript-plugin-css-modules插件,构建时仍然遇到错误,而这个解决了我的问题。 - Mark H
19
请将 declaration.d.ts 文件加入到 tsconfig.json 文件中,写成 { "include": ["./declaration.d.ts"] } 的形式。 - dhaker
一个小细节,最好将其键入为 Record<string, string | undefined>,因为并非每个有效的键都有匹配项。 - Etheryte
1
@Etheryte 是的,你说得对,包括未定义类型更接近现实,但这也会对开发者体验产生很大影响,需要在权衡中做出选择。 - Kalle

94

这是一份完整的配置,适用于我(我刚刚花费了一个小时痛苦地试错 - 以防有人遇到同样的问题):

TypeScript + WebPack + Sass

webpack.config.js

module.exports = {
  //mode: "production", 
    mode: "development", devtool: "inline-source-map",

    entry: [ "./src/app.tsx"/*main*/ ], 
    output: {
        filename: "./bundle.js"  // in /dist
    },
    resolve: {
        // Add `.ts` and `.tsx` as a resolvable extension.
        extensions: [".ts", ".tsx", ".js", ".css", ".scss"]
    },
    module: {
        rules: [

            { test: /\.tsx?$/, loader: "ts-loader" }, 

            { test: /\.scss$/, use: [ 
                { loader: "style-loader" },  // to inject the result into the DOM as a style block
                { loader: "css-modules-typescript-loader"},  // to generate a .d.ts module next to the .scss file (also requires a declaration.d.ts with "declare modules '*.scss';" in it to tell TypeScript that "import styles from './styles.scss';" means to load the module "./styles.scss.d.td")
                { loader: "css-loader", options: { modules: true } },  // to convert the resulting CSS to Javascript to be bundled (modules:true to rename CSS classes in output to cryptic identifiers, except if wrapped in a :global(...) pseudo class)
                { loader: "sass-loader" },  // to convert SASS to CSS
                // NOTE: The first build after adding/removing/renaming CSS classes fails, since the newly generated .d.ts typescript module is picked up only later
            ] }, 

        ]
    }
}; 

同时在你的项目中添加一个declarations.d.ts文件:

// We need to tell TypeScript that when we write "import styles from './styles.scss' we mean to load a module (to look for a './styles.scss.d.ts'). 
declare module '*.scss'; 

你需要在package.json文件的dev-dependencies中添加所有这些依赖:

  "devDependencies": {
    "@types/node-sass": "^4.11.0",
    "node-sass": "^4.12.0",
    "css-loader": "^1.0.0",
    "css-modules-typescript-loader": "^2.0.1",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "ts-loader": "^5.3.3",
    "typescript": "^3.4.4",
    "webpack": "^4.30.0",
    "webpack-cli": "^3.3.0"
  }

然后你应该在 mystyle.scss 的旁边得到一个 mystyle.d.ts,其中包含你定义的 CSS 类,你可以将其作为 TypeScript 模块导入并像这样使用:


import * as styles from './mystyles.scss'; 

const foo = <div className={styles.myClass}>FOO</div>; 

CSS会自动加载(作为一个style元素注入到DOM中),并包含加密标识符来代替在.scss文件中的CSS类,以隔离页面中的样式(除非您使用:global(.a-global-class) { ... })。请注意,第一次编译将失败,无论您是添加、删除还是重命名CSS类,因为导入的mystyles.d.ts是旧版本,而不是刚刚生成的新版本。只需再次编译即可。

享受吧。


注意:在添加/删除/重命名CSS类后的第一次构建会失败,因为新生成的.d.ts TypeScript模块只有稍后才会被捕获。如何解决这个问题? - Jon Lauridsen
4
可以通过将 ts-loader 设置在 webpack.config.js 规则数组的底部(末尾),来解决这个问题。这样 ts-loader 将会在每次编译时正确地获取 *.scss.d.ts 文件,因为它们会在之前被生成。 - Yan Pak
3
感谢@YanPak,将global.d.ts文件添加到我的src目录,并将ts-loader移到样式加载器下面解决了我的问题。 - lux

87

只需添加文件 typings.d.ts ,其中包含:

declare module "*.module.css";

记得在CSS文件中声明'module'。例如styles.module.css

导入:

import React from 'react';
import styles from './styles.module.css';

3
仅添加 typings.d.ts 文件就可以工作。 - Manohar Reddy Poreddy
10
typings.d.ts 需要放在你的 src 文件夹内或其子文件夹中。另外,如果你使用 SASS,请不要忘记添加 declare module "*.module.scss"; - Martin

66

正如@Kalle所提到的,TypeScript并不知道除了`.ts`或`.tsx`以外的其他文件类型存在,因此如果一个导入语句中包含未知的文件后缀,它会抛出一个错误。

为了避免出现这种 TS 报错情况,我的做法是创建一个 types 文件夹,并在其中添加一个带有 .d.ts扩展名的文件,里面包含了声明信息。

就像这样:

enter image description here

关于 .d.ts 文件

TypeScript 有两种主要类型的文件。 .ts 文件是实现文件,包含类型和可执行代码。这些是产生 .js 输出的文件,也是你通常编写代码的地方。

.d.ts 文件是仅包含类型信息的声明文件。这些文件不会产生 .js 输出;它们只用于类型检查。

您可以在这里阅读更多关于TS声明的内容。


3
完美!谢谢你的 GIF ;) - Touk
确认 'types' 文件夹确实是必需的。 - William Martin
也与*.module.scss一起工作过。 - undefined

36

我正在使用typescript-plugin-css-modules

npm install -D typescript-plugin-css-modules

tsconfig.json

{
  "compilerOptions": {
    "plugins": [{ "name": "typescript-plugin-css-modules" }]
  }
}


谢谢,我建议使用ts配置的解决方案。 - Expert Ngobeni
这个完美地运作了,而且是我找到的最简单的解决方案。 - mircaea

18

这对我有用(我正在使用Typescript + vite)。

  1. 将此添加到项目根文件夹中的declaration.d.ts文件中。

declare module "*.module.css";

declare module "*.module.scss";
  • tsconfig.json中的"include"中添加"declaration.d.ts"

  • {
      "compilerOptions": {...},
      "include": ["src", "declaration.d.ts"],
    }
    

    这将允许声明在src文件夹中工作,而不仅仅是在declarations.d.ts所在的root文件夹中工作。

    您可以直接将declaration.d.ts添加到导入样式的文件夹中,但这是个很糟糕的想法。


    17

    只需添加包含以下内容的declaration.d.ts文件:

    declare module "*.module.css";
    

    记得使用.module.css来声明你的CSS文件。

    导入:

    import styles from './styles.module.css'
    

    如果使用Sass,请将.css语句替换为.scss


    16

    100% 可行。

    只需在您的项目中添加一个名为“declarations.d.ts”的文件,并在其中添加以下行:

    declare module '*.css';
    

    1
    它对我不起作用。 - SalahAdDin

    9

    如果您在使用tsconfig.json路径配置,请注意

    请注意,如果您将这些解决方案与tsconfig路径一起使用以缩短导入路径,则需要进行额外的配置。

    如果您使用类似于以下的path tsconfig:

    {
      "compilerOptions": {
        "paths": {
          "style/*": [ "src/style/*" ],
        }
      }
    }
    

    所以你可以做:

    import { header } from 'style/ui.scss';
    

    然后你还需要在Webpack上添加 modules resolve 的配置,像这样:

    module.exports = {
      ...
      resolve: {
        ...
        alias: {
          style: path.resolve(__dirname, 'src', 'style')
        }
      }
    }
    

    确保路径根据您的设置进行设置。

    这样可以使webpack知道应该在哪里查找,因为它认为新的导入路径实际上是一个模块,所以默认情况下它将查找node_modules目录。有了这个配置,它就知道在哪里查找并找到它,从而使构建工作正常。


    8

    以上的答案对我没有用,我正在使用 SCSS + TS + React(如果你也在使用,请看这个答案)。我的做法是:

    在 src 文件夹中创建一个 declarations.d.ts 文件,并添加以下代码:

    declare module "*.scss" {
      const content: { [className: string]: string };
      export = content;
    }
    

    然后在src文件夹中创建了tsconfig.json文件,并添加了以下代码

    {
      "compilerOptions": {
        "plugins": [{ "name": "typescript-plugin-css-modules" }]
      },
      //add the following
      "include": ["src/**/*.ts", "declarations.d.ts"]
    }
    

    然后最终安装插件,使用命令npm i typescript-plugin-css-modules

    这样IT技术就可以奏效啦 :)


    谢谢,这使我能够将scss变量同步到基于JS的主题框架中。 - r0ber7

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接