How to use TailwindCSS's resolve config with Typescript

Viewed 3765

I'm importing the following code into my .tsx component:

import resolveConfig from 'tailwindcss/resolveConfig';
import tailwindConfig from '../../../tailwind.config.js';

const fullConfig = resolveConfig(tailwindConfig)

If I then reference fullConfig.theme.colors["blue"]["700"] I get an error saying:

Element implicitly has an 'any' type because expression of type '"blue"' can't be used to index type 'TailwindThemeColors'. Property 'blue' does not exist on type 'TailwindThemeColors'.

The Tailwind type definition for colors is:

export type TailwindColorValue = string | TailwindColorGroup | TailwindColorFunction;

export interface TailwindValuesColor {
    readonly [key: string]: TailwindColorValue;
}

Since the type does not explicitly say that "blue" is a value and that it could be an object, that's the cause of the error (I think).

An additional problem I'm getting is an error in my tsconfig.json, because I'm importing a JS file. Adding "exclude": ["tailwind.config.js"] doesn't solve the issue.

I'm pretty sure this is a Typescript issue more than a Tailwind one...

My questions are:

  1. How can I make Typescript aware of the "blue" property on the colors in my tailwind theme, and
  2. How can I stop the tsconfig error from importing the JS config?

Any help in resolving this would be hugely appreciated.

2 Answers

This looks like it's a problem occurring because of the union types that Tailwind has typed its resolveConfig return value with. Essentially, because of the union, TS doesn't know whether the value of fullConfig.theme.colors is an object or a getter function (and likewise, whether fullConfig.theme.colors["blue"] is a string, color-group object or a color function) - presumably this is because this decision is left to the user's tailwind.conf implementation choices, and Tailwind doesn't infer it.

The easiest way to get around this would probably be to write your own function that takes the union-typed value you're trying to use and returns only the specific type you want, eg.

import _ from 'lodash';

const asColorObject = (
  input: TailwindThemeColors | undefined,
): Exclude<typeof input, Function | undefined> => {
  if (_.isFunction(input) || !input) throw new Error();
  return input;
};

const fullConfig = resolveConfig(tailwindConfig);
const colorBlue = asColorObject(fullConfig.theme.colors)['blue'];

In terms of why importing your Tailwind config is giving you errors, check your allowJs and checkJS keys in compilerOptions in your TSConfig. Otherwise, if you're using Create React App, it won't be possible this way at all since you can't import files from outside the src folder.

I think maybe you can using the tailwind default config type to resolve like this:

import resolveConfig from 'tailwindcss/resolveConfig'
import tailwindConfig from 'tailwind.config'
import { TailwindConfigDefault } from 'tailwindcss/tailwind-config-default'
import { TailwindConfig } from 'tailwindcss/tailwind-config'

export default resolveConfig(tailwindConfig as TailwindConfig) as TailwindConfigDefault & {
  theme: {
    colors: {
      main: {
        light: 'var(--color-main-light)'
        dark: 'var(--color-main-dark)'
      }
    }
  }
  // other custom config
}
Related