Attention TypeScript Developers: Are You Linting Your Files All Wrong?

Transform Your TypeScript Projects Overnight with This Mind-Blowing Linting Secret!

Lakindu Hewawasam
Bits and Pieces

--

Do you lint your TypeScript code?

If you have worked in a large-scale React/Node TypeScript project, you’ve likely had to follow the linting rules defined in your project. For example, I’m sure you’ve run into linting errors like this:

Figure: A TypeScript Linting Error

Sometimes, you run into these errors even though you want to define your code using such syntax for a specific set of files. For example, I’ve used Snake Case to define types for a React Context when my ESLint configuration requires Type definitions in Pascal Case. However, if you navigate the Redux documentation, you’ll see a best practice recommending using Snake Case to define types for a particular Action.

But, when working in a monolithic environment such as a single React or Node.js application, you’re bound to a single linting configuration. In this case, I was bound to a single ESLint configuration as all the files I managed belonged to a single repository. This approach works fine when you’re adhering to your linting config. However, there can be a time when you define a file that does not comply with a linting standard, just like the example we discussed earlier.

In these cases, you’re left with the following options:

  1. You can turn off certain linting rules in your ESLint configuration file. However, by doing so, you turn it off globally across your project.
  2. You can turn off the linting rule by adding @ts-ignore or eslint-disable-<<rule>> onto your TypeScript file. However, by doing so, you add unnecessary lines of code onto your file, hindering readability and maintainability.
  3. Finally, you can add an overrides array in your ESLint configuration and specify a set of rules that are applied only to a specific subset of files, as shown below:
{
"extends": "eslint:recommended",
"rules": {
// Your default rules
},
"overrides": [
{
"files": ["*.test.js", "*.spec.js"],
"rules": {
"no-unused-expressions": "off"
}
}
]
}

However, as you can see, you have to manually update the files array and specify the files to which you want the rules to apply. This can be hard to maintain as there are chances where you forget to add the file to this array, or it can even become more burdensome to read if you add around 200 entries to this array.

So, this problem ultimately leads to poor code being shipped to production while hindering developer productivity. Rather than a global ESLint configuration, we require a solution that lets us maintain a single ESLint configuration per TypeScript file.

And that’s exactly what tools like Bit let us achieve.

Figure: Bit

Independent Component Development with Bit

Think of Bit as a build system for composable software.

You can design and build anything in terms of simple modular components that are completely independent of each other. It does this by ensuring that a single component has its own isolated runtime (environment) that lets you design, develop, test, and build a component in complete isolation. If this sounds confusing, look at the diagram below:

Figure: A component tree in Bit

The diagram above showcases four components, each having its own version. All of these components were designed, and built in complete isolation. Doing so lets us handle each component’s configuration information independently.

Yes, you thought right. You can use independent linting configurations per Bit component.

So, during build time, each component would be linted using its linting configuration. This lets you address all the issues we discussed at the start without any additional configuration!

Building Custom With Custom ESLint Configurations with Bit

Well, how do we do this?

It’s easy! In fact, it’ll take less than 5 minutes to create a component with a custom ESLint configuration.

Step 01: Pre-requisites

First things first. You’ll need to install Bit. You can do this by running the command:

npx @teambit/bvm install

To confirm the installation, run the command bit --version and you should see the output I've shown below.

Figure: Confirming the Bit installation

Step 02: Initializing an empty workspace

After that, you’ll need to set up a workspace in Bit. This lets you build your components locally and work on them! What’s interesting about this is that a Bit workspace does not have any runtime built into it. It’s not the same thing as running npx create-react-app. Instead, you must utilize Bit Environment Components to provide a runtime for your component.

Yes, every Bit component has an environment component

Figure: An environment component being used in Bit

As you can see above, all three React components use the my-react-env component that provides the React runtime for each component to work. Okay, now that we have a basic understanding of an environment, let's create the Bit workspace using the command:

bit init

After you run the command, you should see the following files:

Figure: Creating a Bit project successfully

Step 03: Creating a reusable ESLint Component

Next, we’ll have to create a reusable linter component that we can apply to one or group of Bit components. This component will hold an instance of ESLint that we can directly apply to a set of components or simply one!

To create a Linter component, run the commands:

bit install @teambit/defender.eslint-linter

This will install the Linter libraries onto your workspace so that you can start configuring it. Next, you’ll need to create a custom environment. In this demo, let’s create a custom React environment:

bit fork teambit.react/react-env

This will create a component as shown:

Figure: React Environment

As you can see, your React Environment has a config directory that holds the eslintrc.js file. In this file, you can configure all the ESLint configurations you want running in the group of components you're using the environment with. For example, let's include the linting configuration:

// force bit to recognize this as dependency
require('@teambit/react.eslint-config-bit-react');

module.exports = {
extends: [require.resolve('@teambit/react.eslint-config-bit-react')],
settings: {
'mdx/code-blocks': true,
jest: {
version: 29,
},
react: {
version: '18.0',
},
},
rules: {
'no-console': 'off'
},
};

As show above, we’re extending the pre-build ESLint configuration and disabling error flag that ESLint throws for the usage of console.log().

To test this rule out, let’s create a React component that uses the environment we just created.

Step 04: Creating a React Component

To create the React component that uses the custom env, run the command:

bit create react sample/button --env react-env

Next, let’s open the button.tsx file and include a console.log to test out the linter:

import type { ReactNode } from 'react';

export type ButtonProps = {
children?: ReactNode;
};

export function Button({ children }: ButtonProps) {
console.log('Hello');
return (
<div>
{children}
</div>
);
}

Next, lint your component using the command:

bit lint sample/button

You’ll notice that there’s no error popping up:

Figure: Linting the component successfully

Next, go ahead and uncomment the rule and re-lint your component. You should see the error pop up:

Figure: Showing the linting error

This showcases that our custom ESLint configuration is now up and running. Likewise, you can customize this as you need and use environments to isolate your components that use different linting configurations.

Step 05 — Customizing Component Environment Files In an Independent Space

Okay, now, after you’ve created a custom base ESLint for all your components, you’ll likely want to proceed and have custom ESLint variations per component, right? I mean, that’s what we’re trying to solve?

Let’s create another React component:

bit create react sample/typography --env react-env

Afterward, run the following command:

bit ws-config write

This command will create ESLint configurations for all components in your workspace:

Figure: Viewing the generated Linter configuration

So, what this means is that you now have the capability of customizing ESLint configurations on a per-component basis.

How independent is that?

Okay, now, let’s add a rule that will only apply to this Typography component:

// bit-generated-eslint-config
{
"extends": [
"./../../../node_modules/.cache/.eslintrc.bit.586ffdd814fb4c3cfe69ed772f82ab3aa4bb7382.json"
],
"rules": {
"no-alert": "off",
}
}

Let’s update the typography.tsx to add an alert:

import type { ReactNode } from 'react';

export type TypographyProps = {
children?: ReactNode;
};

export function Typography({ children }: TypographyProps) {
alert('Hello');
return (
<div>
{children}
</div>
);
}

Next, let’s lint the component using:

bit lint sample/typography

You’ll see the output below:

Figure: Showcasing the custom per-component linter

Wrapping up

Well, there we have it!

It didn’t even take 5 minutes to set up a custom linter and adapt it to independent components. Likewise, you can manage your components with independent ESLint configurations and ensure your development productivity is as optimal as ever!

If you want to explore the code we built, check out my Bit Cloud Scope!

Thank you for reading.

--

--