create-react-app: Typescript declare field causes transpile to fail

Describe the bug

Using typescript declare to fix the type of the context field in class components causes the build to fail with

./src/App.tsx
SyntaxError: C:\Users\wiles\Documents\git\broken-cra\src\App.tsx: TypeScript 'declare' fields must first be transformed by @babel/plugin-transform-typescript.
If you have already enabled that plugin (or '@babel/preset-typescript'), make sure that it runs before any plugin related to additional class features:
 - @babel/plugin-proposal-class-properties
 - @babel/plugin-proposal-private-methods
 - @babel/plugin-proposal-decorators
  10 | class MyComponent extends React.Component {
  11 |   static contextType = MyContext;
> 12 |   declare context: React.ContextType<typeof MyContext>;
     |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  13 | 
  14 |   render() {
  15 |     return <span>{this.context.data || "No context"}</span>;

Did you try recovering your dependencies?

Yes

npm --version
6.14.4

Which terms did you search for in User Guide?

typescript declare transpile

Environment

npx create-react-app --info
npx: installed 98 in 23.993s

Environment Info:

  current version of create-react-app: 3.4.1
  running from C:\Users\wiles\AppData\Roaming\npm-cache\_npx\2632\node_modules\create-react-app

  System:
    OS: Windows 7 6.1.7601
    CPU: (8) x64 Intel(R) Core(TM) i7-2820QM CPU @ 2.30GHz
  Binaries:
    Node: 12.16.1 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.4 - C:\Program Files (x86)\Yarn\bin\yarn.CMD
    npm: 6.14.4 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Internet Explorer: 11.0.9600.18123
  npmPackages:
    react: ^16.13.1 => 16.13.1
    react-dom: ^16.13.1 => 16.13.1
    react-scripts: 3.4.1 => 3.4.1
  npmGlobalPackages:
    create-react-app: Not Found

Steps to reproduce

  1. Add the following component to a React hierarchy
interface MyContextValue {
  data?: string;
}
const MyContext = React.createContext<MyContextValue>({});

class MyComponent extends React.Component {
  static contextType = MyContext;
  declare context: React.ContextType<typeof MyContext>;

  render() {
    return <span>{this.context.data || "No context"}</span>;
  }
}
  1. Try to build the app with npm start or npm run build

Expected behavior

That the build will succeed

Actual behavior

Build fails with

./src/App.tsx
SyntaxError: C:\Users\wiles\Documents\git\broken-cra\src\App.tsx: TypeScript 'declare' fields must first be transformed by @babel/plugin-transform-typescript.
If you have already enabled that plugin (or '@babel/preset-typescript'), make sure that it runs before any plugin related to additional class features:
 - @babel/plugin-proposal-class-properties
 - @babel/plugin-proposal-private-methods
 - @babel/plugin-proposal-decorators
  10 | class MyComponent extends React.Component {
  11 |   static contextType = MyContext;
> 12 |   declare context: React.ContextType<typeof MyContext>;
     |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  13 | 
  14 |   render() {
  15 |     return <span>{this.context.data || "No context"}</span>;

Reproducible demo

https://github.com/benwiles1/broken-cra

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 27
  • Comments: 19

Commits related to this issue

Most upvoted comments

I also meet this question,It’s via babel plugins add -> @babel/plugin-transform-typescript and options -> “allowDeclareFields”: true the following my .babelrc file

{
 "presets": [
    "react-app"
  ],
  "plugins": [
    [
      "@babel/plugin-transform-typescript",
      {
        "allowDeclareFields": true
      }
    ]
  ]
}

will need -> yarn add @babel/plugin-transform-typescript -D

You can work around this using CRACO. I’ll share my configuration, but first a warning: I barely understand web pack’s configuration and figured this out through trial and error. Be wary.

// craco.config.js

const {
    loaderByName,
    getLoader,
} = require("@craco/craco");

module.exports = {
    webpack: {
        configure: function (webpackConfig) {
            const babelLoader = getLoader(
                webpackConfig,
                loaderByName("babel-loader")
            ).match.loader;
            babelLoader.options.presets.push([
                "@babel/preset-typescript",
                {allowDeclareFields: true},
            ]);

            return webpackConfig;
        }
    }
};

Is there a workaround without ejecting or additional dependencies? If you leave out the declaration it is literally telling you to add it, but it can’t actually be used…

CRA hides and manages those for you, so unless you want to eject or play around with something like Craco, you have to wait for that PR to land.