Monday, October 14, 2019

Absolute import with Next.js and TypeScript

Make Next.js project TypeScript-capable by following this:

https://nextjs.org/blog/next-9#built-in-zero-config-typescript-support


Create shared directory in project’s root directory

Under shared directory, create randomizer.ts:

export function randomizer() {
    return 4;
}


Create deeply/nested directories under components directory

Under components/deeply/nested directory, create SampleComponent.tsx:

import React from "react";

// import { randomizer } from "../../../shared/randomizer";

import { randomizer } from "love/randomizer";

export function SampleComponent() {
    const a = randomizer();

    return (
        <div>
            <strong>I am strong {a}</strong>
        </div>
    );
}

Edit pages/index.tsx, then add SampleComponent, e.g.,

<p className="description">
    To get started, edit <code>pages/index.js</code> and save to
    reload.
</p>

<SampleComponent />


Add this import statement to pages/index.tsx:

import { SampleComponent } from "../components/deeply/nested/SampleComponent";

Edit tsconfig.json and add the following under compilerOptions:

"baseUrl": ".",
"paths": {
    "love/*": ["shared/*"]
}

Webpack must be informed of TypeScript’s paths, this can be done by modifying webpack’s resolve.alias object. To modify webpack’s configuration, create next.config.js in project’s root directory, then put the following:

const path = require("path");

module.exports = {
    webpack(config, { dev }) {
        config.resolve.alias = {
            ...config.resolve.alias,
            love: path.resolve(__dirname, "shared")
        };

        return config;
    }
};

Run yarn dev, everything shall work.


To avoid manual synchronization of tsconfig’s paths and webpack’s resolve.alias, use the following helper: (sourced from: https://gist.github.com/nerdyman/2f97b24ab826623bff9202750013f99e)

Create resolve-tsconfig-path-to-webpack-alias.js in project's root directory:

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

/**
 * Resolve tsconfig.json paths to Webpack aliases
 * @param  {string} tsconfigPath           - Path to tsconfig
 * @param  {string} webpackConfigBasePath  - Path from tsconfig to Webpack config to create absolute aliases
 * @return {object}                        - Webpack alias config
 */
function resolveTsconfigPathsToAlias({
    tsconfigPath = './tsconfig.json',
    webpackConfigBasePath = __dirname,
} = {}) {
    const { paths } = require(tsconfigPath).compilerOptions;

    const aliases = {};

    Object.keys(paths).forEach((item) => {
        const key = item.replace('/*', '');
        const value = resolve(webpackConfigBasePath, paths[item][0].replace('/*', '').replace('*', ''));

        aliases[key] = value;
    });

    return aliases;
}

module.exports = resolveTsconfigPathsToAlias;

Change next.config.js to following:

const path = require("path");

const resolveTsconfigPathsToAlias = require("./resolve-tsconfig-path-to-webpack-alias");

module.exports = {
    webpack(config, { dev }) {
        config.resolve.alias = {
            ...config.resolve.alias,
            ...resolveTsconfigPathsToAlias()
        };

        return config;
    }
};

Run yarn dev, everything shall work.


Sample working code: https://github.com/ienablemuch/experiment-react-nextjs-typescript-absolute-import