Coulter Peterson
Coulter Peterson

Reputation: 101

Unexpected token "<" expected "{" when passing Interfaces to React Class Component

Ran into an issue in my Laravel 8/Babel/Mix/React/TypeScript environment where a regular React class component is producing a compile error, since I've migrated the project to TypeScript and started passing an empty props and simple state interface to the component.

I've tried passing empty JSON objects, commenting out various import statements, moving the export default call below the function, but no dice so far.

The compile error:

Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: /Users/.../MyComponent.tsx: Unexpected token, expected "{" (10:54)

   8 | import { IState } from '../interfaces';
   9 |
> 10 | export default class MyComponent extends React.Component<{}, IState> {
     |                                                         ^
  11 |
  12 |     constructor(props:any) {
  13 |       super(props);
    at Object._raise (/Users/.../node_modules/@babel/parser/lib/index.js:796:17)

MyComponent.jsx snippet:


import { IState } from '../interfaces';

export default class MyComponent extends React.Component<{}, IState> {

    constructor(props:any) {
      super(props);
      this.state = dummyData;
    }
    ...

Simplified interfaces.ts:

export interface IState {
    tasks: {
        [key: string]: ITask
      };
    columns: {
        [key: string]: IColumn
    };
    columnOrder: string[];
}

webpack.mix.js

const mix = require('laravel-mix');

require('laravel-mix-purgecss');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.js('resources/js/app.ts', 'public/js')
    .react()
    .sass('resources/sass/app.scss', 'public/css')
    .postCss('resources/css/app.css', 'public/css', [
        require('tailwindcss')(),
    ])
    .setResourceRoot("/") // This allows easy resource linking from React components
    .purgeCss({
        //enabled: mix.inProduction(),
        enabled: true,
        folders: ['resources', 'app'],
        extensions: ['html', 'js', 'ts', 'tsx', 'php', 'vue'],
    });

mix.extend('customAdditions', function(webpackConfig, ...args) {
    webpackConfig.resolve.extensions.push('.ts', '.tsx');
});

mix.customAdditions();

.babelrc

{
    "plugins": ["transform-class-properties"],
    "presets": [["@babel/preset-env", {"modules": false}],
    "@babel/preset-react"]
}

tsconfig.json

{
  "compilerOptions": {
    "target": "es5", 
    "module": "commonjs", 
    "lib": ["dom", "es2017"],   
    "jsx": "react",      
    "strict": true, 
    "noImplicitAny": true,   
    "moduleResolution": "node",  
    "esModuleInterop": true,   
    "skipLibCheck": true,    
    "forceConsistentCasingInFileNames": true ,
    "watch": true,
  }
}

Update 1: Additional Things Tried to No Avail:

Upvotes: 3

Views: 1223

Answers (1)

Coulter Peterson
Coulter Peterson

Reputation: 101

Figured it out: needed to swap @babel/preset-react in my .babelrc for @babel/preset-typescript, after adding those presets with yarn add @babel/preset-typescript --dev. Shoutout to Leon Gaban's answer for pointing me in that direction 🙌

The new .babelrc:

{
    "plugins": ["transform-class-properties"],
    "presets": [
        ["@babel/preset-env", {"modules": false}],
        "@babel/preset-typescript" // <--
    ]
}

This would also fix it if someone was experiencing the error interface is a reserved word... when declaring an interface right before their class component.

Upvotes: 2

Related Questions