Search code examples
typescriptwebpackwebpack-dev-servertypescript-typingswebpack-merge

TS2345 Argument of type is not assignable to parameter of type: using one module that's declared in another module


I've been trying to figure out an issue using Webpack, webpack-merge, and separate development Webpack files using TypeScript, and I've sort of realized that I need to declare a type alias function that is passed from a webpack.common.ts file to a webpack.dev.ts file.

Here's what I was trying to do originally. I have a webpack.common.ts file that loads in a basic configuration:

import { Configuration } from 'webpack';
import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin';

const common: Configuration = {
  module: {
    rules: [
      {
        exclude: /node_modules/,
        test: /\.ts(x?)$/,
        use: [{ loader: 'ts-loader' }],
      },
      {
        enforce: 'pre',
        loader: 'source-map-loader',
        test: /\.js$/,
      },
    ],
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
    plugins: [new TsconfigPathsPlugin({})],
  },
};

export default common;

I then have separate prod and dev Webpack files that use webpack-merge. Here is webpack.dev.ts:

import { Configuration } from 'webpack';
import common from './webpack.common';
import path from 'path';

const __dirname = path.resolve();

const dev: Configuration = merge(common, {
  devServer: {
    compress: true,
    contentBase: path.join(__dirname, 'dist/dev'),
    historyApiFallback: true,
    hot: true,
    open: true,
    port: 9000,
  },
  devtool: 'source-map',
  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM',
  },
  mode: 'development',
});

export default dev;

This results in an error inside the merge method:

Argument of type 'import(".../node_modules/@types/webpack/index").Configuration' is not assignable to parameter of type 'import(".../node_modules/@types/webpack-merge/node_modules/@types/webpack/index").Configuration'.

Based on that, it looks webpack-merge has its own Webpack typings, and Webpack and webpack-merge are using two different Configuration types.

I tried updating the webpack-merge typings so that it would import Webpack's typings rather than its own set.

Inside webpack-merge's index.d.ts file:

// import { Configuration } from 'webpack'; 
import { Configuration } from '../webpack';

Doing that, I now get a different error from inside the merge method in webpack.dev.ts:

Argument of type '{ devServer: { compress: boolean; contentBase: string; historyApiFallback: boolean; hot: boolean; open: boolean; port: number; }; devtool: "source-map"; externals: { 'react': string; 'react-dom': string; }; mode: "development"; }' is not assignable to parameter of type 'Configuration'.

Hovering over merge in VS Code, this is type information I get for it:

(alias) merge(...configs: Configuration[]): Configuration

Should I be updating the import path for webpack-merge so that it uses the same type declarations? And how can I update the merge typing to accept these webpack-dev-server types (without having to use any)?


Solution

  • Extending the TypeScript typing for the webpack Configuration to define devServer as recommended in this DefinitelyTypes issue response by uipoet (and transcribed here) worked for me.

    import { Configuration as WebpackConfiguration } from "webpack";
    import { Configuration as WebpackDevServerConfiguration } from "webpack-dev-server";
    
    interface Configuration extends WebpackConfiguration {
      devServer?: WebpackDevServerConfiguration;
    }
    
    export const configuration: Configuration = {
      ...
      devServer: {
        historyApiFallback: true,
        hot: true,
        port: 3000
        ...
      }
      ...
    }
    

    Tested with @types/webpack 5.28.0 and @types/webpack-dev-server 3.11.4.

    References: