Generating TypeScript definitions for CSS Modules using SASS

Generating TypeScript definitions for CSS Modules using SASS

Photo by ariel sion

CSS Modules can be a great tool for maintaining styles in a large codebase. It works much like vanilla CSS with the primary difference being all class names are local by default. This works well in modern component-based architectures where a single style module can live alongside the component that relies on those styles.

Since a CSS Module is vanilla CSS by default, it doesn’t introduce a steep learning curve. But in a large codebase, it’s not desirable to repeatedly define specific values (such as color hexes), especially if using a design system. There are approaches specific to CSS Modules for adding support for variables, but SASS is a reliable technology to add support not only for variables but many other useful features.

Beyond styling an application, TypeScript can be a great tool for maintaining a large codebase in general. Providing type definitions for functions or components can make the APIs more discoverable to newcomers and dissuade improper usage.

CSS Modules and TypeScript can each be used on their own. But what about integrating CSS Modules, SASS and TypeScript to add type-safety around the usage of the styles?

🛠 Existing Tooling

There are great articles and tools that already exist in the world of CSS Modules and TypeScript.

This post is a great overview for using both CSS Modules and TypeScript with webpack. The easiest way is to not integrate the two together at all by using require instead of import. The styles object will be typed as any offering no type-safety.

To add full type-safety requires defining .d.ts (type definition) files for the corresponding styles. This can be done by manually creating this file and defining all of the corresponding class names. It’s tedious, duplicative work and error prone which defeats the purpose of type-safety.

A middle ground approach is to define a module definition that applies to all style imports. It won’t catch invalid class names or provide the type-ahead in supported editors but it is an improvement on any. For example, a file named css-modules.d.ts would achieve this:

declare module "*.scss" {
  const styles: { [className: string]: string };
  export default styles;
}

But what about having full type-safety without manually providing the types?

typings-for-css-modules-loader

typings-for-css-modules-loader is a drop-in replacement for css-loader (necessary for CSS Modules) to automatically generate the type definitions for your CSS Modules in webpack. Since it’s a drop-in, it should also support SASS!

But what if you’re not using webpack?

typed-css-modules

typed-css-modules is a command-line interface (CLI) for generating type definitions for CSS Modules. After installing the package, it can be run outside of webpack. This is useful if you’re not using webpack, experimenting with adding type definitions to see what it would look like, or want to run it in one-off situations.

But what if you’re using SASS?

🎁 typed-scss-modules

As the name suggests, typed-scss-modules is heavily inspired by typed-css-modules. It’s a CLI that generates type definitions focused on supporting CSS Modules written using SASS.

Example of writing a SASS CSS Module while generating the corresponding type definitions. Example of writing a SASS CSS Module while generating the corresponding type definitions.

Getting started

Install the package as a development dependency since the type definitions should only need to be generated in development:

yarn add -D typed-scss-modules 

Now, the types can be generated by providing the directory (or glob pattern):

yarn tsm src/

Listing differences

This option is inspired by Prettier’s list-different option which is particularly useful in Continuous Integration to ensure all proposed code changes adhere to the proper formatting. Similarly, in this case, if a type definition doesn’t match what would be automatically generated, the command will fail and list the files that are invalid.

yarn tsm src/ --listDifferent

Listing the differences is only one of many options. See the README for a full list of options for changing the naming convention, the type definition format, handling aliases, included search paths and other options.

📚 Summary

There is a spectrum for how CSS Modules can be integrated with TypeScript. Depending on the specific use case, existing codebase, and other technologies being used there are various tools that can be used that fit the different needs.

If you’re specifically using CSS Modules, SASS, and TypeScript and want to see what generating type definitions might look like I encourage you to give typed-scss-modules a try!

For more content on topics like this, React, TypeScript, JavaScript, or Design Systems check out the Rubber Ducking podcast.