How to use TypeScript in your Shopware plugins

In StackOverflow’s 2020 Developer Survey TypeScript surpassed Python as the most loved language and is now in second place.

This has its reasons. TypeScript adds type definitions to JavaScript, which is great for large codebases, scaling and developing without despair! Mighty Microsoft, as its maintainer, facilitated its growth and tool availability.

So, only one question remains: how to use it in a Shopware plugin? Stay tuned and let me show you.

We are going to write an administration module that will load a Shopware component with a TypeScript interface. You can get the complete plugin from GitHub.

First, you need a basic plugin, which you can create with Shopware’s console command bin/console plugin:create NAME . For this example I used the name KplngiTypeScript. Now, we have to create a few files. Place a main.js file in the src/Resources/app/administration/src/ directory of your plugin, this is the entry for our module. Into this you need to add the line import './module/kplngi-type-script/index.ts'; and create the corresponding file /src/Resources/app/administration/src/module/kplngi-type-script/index.js with the following code:

import './components/kplngi-type-script-index/index.ts'

Shopware.Module.register('kplngi-type-script', {
    type: 'plugin',
    name: 'KplngiTypeScript',
    color: '#ff3d58',
    icon: 'default-shopping-paper-bag-product',
    title: 'Type Script',
    description: 'Type Script',

    routes: {
        index: {
            component: 'kplngi-type-script-index',
            path: 'index'
        }
    },

    navigation: [{
        label: 'Type Script',
        color: '#ff3d58',
        path: 'kplngi.type.script.index',
        icon: 'default-shopping-paper-bag-product',
        position: 100,
        parent: 'sw-catalogue'
    }]
});

This registers a new module and a link named ‘Type Script’ is added to the catalogue navigation in the administration.

Next, we will create the component. Create the file src/Resources/app/administration/src/module/kplngi-type-script/components/kplngi-type-script-index/index.ts with this content:

import template from './kplngi-type-script-index.html.twig';

Shopware.Component.register('kplngi-type-script-index', {
    template
});

And the template file in the same folder, kplngi-type-script-index.html.twig :

<sw-page class="kplngi-type-script-index">
   <template #content>
       <div class="kplngi-type-script-index__content">
           <div>my content</div>
       </div>
   </template>
</sw-page>

This component simply displays ‘my content’.

Now, we can build the project with ./psh.phar administration:build. You might have noticed the file ending .ts (for typescript) is already being used and the Shopware build process did indeed load them like regular .js files. So what happens, if we add actual TypeScript code?

Create a new file src/Resources/app/administration/src/module/kplngi-type-script/components/kplngi-type-script-index/SwNumberField.ts with the following code:

export default interface SwNumberField {
    label: string,
    value: number
}

 From this we are exporting a TypeScript interface with the requirements of having a label, which is a string, and a value, which is a number.

Import it into the components index.ts file:

import template from './kplngi-type-script-index.html.twig';
import SwNumberField from './SwNumberField';
...

This does nothing, except giving our component access to the interface. When ./psh.phar administration:build is executed again, an error is thrown: Module parse failed: Unexpected token. You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders. Alright, so we need a TypeScript loader. A look into Shopware’s administration webpack.config.js reveals that .ts files are indeed being considered when building the project and are loaded via the babel-loader. The thing is, the @babel/preset-env is used to do the transpiling and is not capable of TypeScript. But Babel, as the name suggests, is also versed in TypeScript, all we need is the right preset.

For this, we will use npm to load the needed packages. You can create a new npm package for your plugin in the src/Resources/app/administration folder with npm init -y.

Now, we have to install two packages with npm install --save @babel/preset-typescript and npm install --save @babel/core. But since Shopware does not know about these packages yet, you will have to create a webpack.config.js file in the src/Resources/app/administration/build directory and add the following:

const { join } = require('path');

module.exports = () => {
   return {
       module: {
           rules: [
               {
                   test: /\.ts$/,
                   loader: 'babel-loader',
                   options: {
                       presets: [join(__dirname, '..', 'node_modules', '@babel', 'preset-typescript')]
                   }
               }
           ]
       }
   };
}

This extends Shopware’s standard Webpack configuration. .ts files are still being loaded via the babel-loader, only with the help of the preset we just installed, it being loaded directly from the node_modules folder.

Now, let’s try ./psh.phar administration:build again. No errors! So let us use our TypeScript interface. In the component’s index.ts file add the following to the register function’s options:

computed: {
   fieldData(): SwNumberField {
       return {
           label: 'My Label',
           value: 3
       }
   }
}

Finally, you need to modify the kplngi-type-script-index.html.twig by replacing <div>my content</div> with <sw-number-field v-bind="fieldData"></sw-number-field>

Now, the data, that is passed to the sw-number-field, is coded against a TypeScript interface.