2

I have a React build configuration using Webpack, ESlint, Prettier and TypeScript.

When I run the build it looks like I'm getting TypeScript linting errors that should be nullified by the @typings/react or @typings/react-dom packages.

Looking at the documentation I shouldn't need to define accessibility modifiers or return types for React Components: https://www.typescriptlang.org/docs/handbook/jsx.html

This seems like the same issue as here: Missing return type on function - in react (typescript) code

but installing the typings packages hasn't resolved the issue for me. Please note, I have also tried removing the @typings/react package as the @typings/react-dom has this as a dependency and this doesn't resolve the issue.

Webpack config

const webpack = require('webpack');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const path = require('path');

const createWebpackConfig = () => {
  const isProd =
    process.env &&
    process.env.NODE_ENV &&
    process.env.NODE_ENV === 'production';

  return {
    entry: ['@babel/polyfill', './index.tsx'],
    mode: isProd ? 'production' : 'development',
    module: {
      rules: [
        {
          enforce: 'pre',
          test: /(\.jsx|\.js|\.ts|\.tsx)$/,
          exclude: /node_modules/,
          loader: 'eslint-loader'
        },
        {
          test: /(\.jsx|\.js)$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: [
                '@babel/preset-react',
                [
                  '@babel/preset-env',
                  {
                    targets: 'cover 99.5% in GB'
                  }
                ]
              ],
              plugins: [
                '@babel/plugin-proposal-class-properties',
                '@babel/plugin-proposal-object-rest-spread'
              ],
              cacheDirectory: true
            }
          }
        },
        {
          test: /(\.tsx|\.ts)$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: [
                '@babel/preset-typescript',
                '@babel/preset-react',
                [
                  '@babel/preset-env',
                  {
                    targets: 'cover 99.5% in GB'
                  }
                ]
              ],
              plugins: [
                '@babel/plugin-proposal-class-properties',
                '@babel/plugin-proposal-object-rest-spread'
              ],
              cacheDirectory: true
            }
          }
        }
      ]
    },
    resolve: {
      modules: [path.resolve(__dirname, '/'), 'node_modules'],
      extensions: ['.ts', '.tsx', '.js', '.jsx'],
      enforceExtension: false
    },
    target: 'web',
    optimization: {
      splitChunks: {
        chunks: 'all',
        maxInitialRequests: Infinity,
        minSize: 0,
        cacheGroups: {
          vendor: {
            test: /[\\/]node_modules[\\/]/,
            name: 'vendor'
          },
          main: {
            name: 'main'
          }
        }
      }
    },
    output: {
      path: path.join(__dirname, 'bundles'),
      filename: '[name].app.js',
      chunkFilename: '[name].bundle.js',
      pathinfo: true
    },
    plugins: [
      new CaseSensitivePathsPlugin(),
      new webpack.DefinePlugin({
        'process.env': {
          NODE_ENV: JSON.stringify(isProd ? 'production' : 'development')
        }
      })
    ]
  };
};

module.exports = createWebpackConfig;

ESLint config

{
  "parser": "@typescript-eslint/parser", // Specifies the ESLint parser
  "extends": [
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin
    "prettier/@typescript-eslint", // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
    "plugin:prettier/recommended" // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
  ],
  "parserOptions": {
    "ecmaVersion": 10, // Allows for the parsing of modern ECMAScript features
    "sourceType": "module", // Allows for the use of imports
    "jsx": true
  },
  "rules": {
    "prettier/prettier": [
      "error",
      {
        "singleQuote": true,
        "endOfLine": "auto"
      }
    ]
  }
}

tsconfig.json

{
  "compilerOptions": {
    "module": "esnext",
    "allowSyntheticDefaultImports": true,
    "allowJs": true,
    "jsx": "preserve"
  },
  "exclude": ["node_modules"]
}

package.json

{
  "name": "reacttypescripttest",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack --config ./webpack.config.js --progress --colors --watch --display-error-details"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/core": "^7.5.5",
    "@babel/plugin-proposal-class-properties": "^7.5.5",
    "@babel/plugin-proposal-object-rest-spread": "^7.5.5",
    "@babel/polyfill": "^7.4.4",
    "@babel/preset-env": "^7.5.5",
    "@babel/preset-react": "^7.0.0",
    "@babel/preset-typescript": "^7.3.3",
    "@types/react": "^16.8.24",
    "@types/react-dom": "^16.8.5",
    "@typescript-eslint/eslint-plugin": "^1.13.0",
    "@typescript-eslint/parser": "^1.13.0",
    "babel-loader": "^8.0.6",
    "case-sensitive-paths-webpack-plugin": "^2.2.0",
    "cross-env": "^5.2.0",
    "eslint": "^6.1.0",
    "eslint-config-prettier": "^6.0.0",
    "eslint-loader": "^2.2.1",
    "eslint-plugin-prettier": "^3.1.0",
    "eslint-plugin-react": "^7.14.3",
    "mini-css-extract-plugin": "^0.8.0",
    "prettier": "^1.18.2",
    "prettier-eslint": "^9.0.0",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "typescript": "^3.5.3",
    "webpack-cli": "^3.3.6"
  },
  "devDependencies": {
    "webpack": "^4.39.1"
  }
}

File causing error (index.tsx)

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  render() {
    return <div>Running!</div>;
  }
}

ReactDOM.render(<App />, document.getElementById('app'));

Errors

  5:3  error    Missing accessibility modifier on method definition render  @typescript-eslint/explicit-member-accessibility
  5:9  warning  Missing return type on function                             @typescript-eslint/explicit-function-return-type
Antfish
  • 1,313
  • 2
  • 22
  • 41

2 Answers2

5

The first error Missing accessibility modifier on method definition render is saying that you did not declare your render method as public or private or any other type (more reading on types. When working with Typescript all class methods need to have be declared accordingly, depending if you want to have them accessible outside your class instance or not. For render method it should be public. Code below should work nicely for you:

public render() {
   return <div>Running!</div>;
}

When it comes the 2nd error your're having, it means that your linter expects the render method to have declared what's the output of its' execution. The correct type for render method is React.ReactNode, so your final code should look like below (assuming you're not importing ReactNode in the import statement earlier)

public render(): React.ReactNode {
   return <div>Running!</div>;
}

Edit 1

Even if TS itself doesn't require all these annotations, the linter you're using does. See their default rules- as you can see, the rules you're having trouble with are not turned off. The solution there would be disabling them in your app eslint config, like below:

"rules": {
   "@typescript-eslint/explicit-function-return-type": "off",
   "@typescript-eslint/explicit-member-accessibility": "off"
}
zhirzh
  • 3,273
  • 3
  • 25
  • 30
Adam Kosmala
  • 925
  • 6
  • 9
  • 1
    I understand what the errors mean, (or at least I understood the first one) but the Typings files should mean that I don't need to annotate the React methods, as in the documentation examples here: https://www.typescriptlang.org/docs/handbook/jsx.html#class-component as they are supposed to inform the compiler. Adding TS annotations to default React Class methods on every component isn't very useful and adds to code cruft. – Antfish Aug 08 '19 at 12:47
  • 1
    Even if TS itself doesn't require all these annotations, the linter you're using does. See their default values https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/src/configs/recommended.json. as you can see, the rules you're having trouble with are not turned off. The solution there would be disabling them in your app eslint config – Adam Kosmala Aug 08 '19 at 13:02
  • Is it possible to disable these ONLY for React classes? I have other classes in my application that it would still be useful to have these turned on for. – Antfish Aug 09 '19 at 07:57
  • Targeting React classes would not be possible, but you can disable specific rules per directory. That may lead to more complex structure and maintaining Eslint rules may be a bit more tricky, but should solve your case. See this https://stackoverflow.com/questions/41316551/eslint-ignore-specific-rule-for-a-specific-directory, there are given two solutions how to do that – Adam Kosmala Aug 09 '19 at 08:09
2

If anyone is running into this AND using a tsx file already. Makre sure that you've configured vscode to associate tsx files with TypescriptReact and not just Typescript. When the tsx file is open, bottom right click on Typescript and set association. vscode status bar

Pieter Venter
  • 861
  • 7
  • 6