Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tailwind 3 broken TS types for config #6422

Closed
sergeyzwezdin opened this issue Dec 12, 2021 · 15 comments
Closed

Tailwind 3 broken TS types for config #6422

sergeyzwezdin opened this issue Dec 12, 2021 · 15 comments

Comments

@sergeyzwezdin
Copy link

What version of Tailwind CSS are you using?

3.0.1

What build tool (or framework if it abstracts the build tool) are you using?

Next.js 12.0.7

What version of Node.js are you using?

For example: v14.18.1

What browser are you using?

Chrome

What operating system are you using?

macOS

Describe your issue

Documentation says to do this to reference tailwind abstraction from JS:

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

const fullConfig = resolveConfig(tailwindConfig)

fullConfig.theme.width[4]
// => '1rem'

fullConfig.theme.screens.md
// => '768px'

fullConfig.theme.boxShadow['2xl']
// => '0 25px 50px -12px rgba(0, 0, 0, 0.25)'

When I try to do this, I have the following errors in the latest version:

import { TailwindConfig } from 'tailwindcss/tailwind-config';
import resolveConfig from 'tailwindcss/resolveConfig'
import tailwindConfig from '../tailwind.config.js'

const fullConfig = resolveConfig(tailwindConfig as unknown as TailwindConfig)

fullConfig.theme.width[4]
// => TS7053: Element implicitly has an 'any' type because expression of type '4' can't be used to index type 'TailwindThemeValue'. Property '4' does not exist on type 'TailwindThemeValue'.

fullConfig.theme.screens.md
// => TS2339: Property 'md' does not exist on type 'TailwindThemeValue'. Property 'md' does not exist on type '(getters: { theme: any; negative: any; colors: any; breakpoints: any; }) => TailwindValues'

fullConfig.theme.boxShadow['2xl']
// => TS7053: Element implicitly has an 'any' type because expression of type '"2xl"' can't be used to index type 'TailwindThemeValue'. Property '2xl' does not exist on type 'TailwindThemeValue'.

2021-12-12-18-34-15

Seems something wrong with types for the latest release. I use tailwind types to describe my components props, so it's kind of critical to fix.

@sachinraja
Copy link
Contributor

Tailwind doesn't come with types, so the issue wouldn't be here. If you're using @types/tailwindcss, you should create an issue at https://github.com/DefinitelyTyped/DefinitelyTyped.

@sergeyzwezdin
Copy link
Author

@sachinraja Got it, thank you for clarification.
Do you consider to have 1st class TS typings for tailwind?
For now it's impossible to use Tailwind 3 because of missing types.

@sachinraja
Copy link
Contributor

I'm not a maintainer, but it looks like there's a discussion here about it

@sergeyzwezdin
Copy link
Author

@sachinraja thanks for the link. It's quite strange that such popular lib doesn't have own integrated TS typings yet.

@benknight
Copy link

benknight commented Aug 22, 2022

Even with the new Tailwind 3.1 release that includes types, I'm still having this issue. I had to rename any files that use resolveConfig to be .jsx to workaround this issue.

@misha-otto
Copy link

We have the same problem. Our temporary solution:

Снимок экрана 2022-08-22 в 17 30 00

@benknight
Copy link

Thanks @Misha-Front that fixed worked. In the meantime I recommend reopening this issue so the missing types can be provided.

@JQuery1996
Copy link

there is a more convenient solution than using any

const fullConfig = resolveConfig(tailwindConfig);
const width = fullConfig.theme?.width as {[key: string]: string};
const colors = fullConfig.theme?.colors as {[key:string]:string};
console.log(width['4]);
// => '1rem'
console.log(colors['red']);
/* 
{
    "50": "#fef2f2",
    "100": "#fee2e2",
    "200": "#fecaca",
    "300": "#fca5a5",
    "400": "#f87171",
    "500": "#ef4444",
    "600": "#dc2626",
    "700": "#b91c1c",
    "800": "#991b1b",
    "900": "#7f1d1d"
}
*/

@morbargig
Copy link

this was my solution

/** @type {Parameters<Parameters<Parameters<import('tailwindcss/plugin')>[0]>[0]['addUtilities']>['0']} */
export const baseFontSizes = {
'.text-14': {
fontSize: '0.875rem',
lineHeight: '1.1428571428571428',
},
'.text-16': {
fontSize: '1rem',
lineHeight: '1.1875',
},
'.text-18': {
fontSize: '1.125rem',
lineHeight: '1.2222222222222223',
},
'.text-20': {
fontSize: '1.25rem',
lineHeight: '1.15',
},
'.text-22': {
fontSize: '1.375rem',
lineHeight: '1.1818181818181819',
},
'.text-24': {
fontSize: '1.5rem',
lineHeight: '1.2083333333333333',
},
'.text-28': {
fontSize: '1.75rem',
lineHeight: '1.1785714285714286',
},
'.text-32': {
fontSize: '2rem',
lineHeight: '1.1875',
},
'.text-34': {
fontSize: '2.125rem',
lineHeight: '1.2058823529411764',
},
'.text-36': {
fontSize: '2.25rem',
lineHeight: '1.1666666666666667',
},
'.text-38': {
fontSize: '2.375rem',
lineHeight: '1.1842105263157894',
},
'.text-42': {
fontSize: '2.625rem',
lineHeight: '1.1904761904761905',
},
'.text-48': {
fontSize: '3rem',
lineHeight: '1.1875',
},
'.text-63': {
fontSize: '3.9375rem',
lineHeight: '1.0793650793650793',
},
};

/** @type {NonNullable<NonNullable<import('tailwindcss').Config['theme']>['extend']>['colors']} */
export const baseColors = {
'accent-bright-red': '#EA0037',
'accent-green': '#06D79D',
'accent-light-blue': '#8CD2FA',
'accent-purple': '#8796FA',
'accent-purple-80': '#E9E9F1',
'background-gray': '#F5F6FA',
'background-pink': '#FCDBE6',
'dark-gray': '#333333',
'disable-gray': '#EAEAEA',
'error-red': '#E91E63',
'header-blue': '#0E0066',
'light-orange': '#F5821F',
'med-gray': '#6B6E77',
'med-gray-20': '#898B92',
'med-gray-40': '#A6A8AD',
'med-gray-90': '#F0F0F1',
'primary-blue': '#232873',
'primary-blue-70': '#BDBFD5',
'primary-orange': '#FF5A23',
'secondary-blue': '#14008E',
'snackbar-bg-color': '#65699D',
'success-green': '#34BB94',
'tag-light-blue': '#EAF1FF',
};

/** @type {NonNullable<NonNullable<import('tailwindcss').Config['theme']>['extend']>['fontWeight']} */
export const baseFontWeight = {
regular: '400',
demibold: '600',
};

/** @type {NonNullable<NonNullable<import('tailwindcss').Config['theme']>['extend']>['boxShadow']} */
export const baseBoxShadow = {
'outer-1': '0px 1px 2px rgba(108, 108, 108, 0.1295)',
'outer-2': '0 0.125rem 0.9375rem rgba(0, 0, 0, 0.0998)',
'outer-3': '0 0.0625rem 0.75rem rgba(0, 0, 0, 0.06)',
'outer-4': ' 0 0.125rem 0.9375rem rgba(0, 0, 0, 0.1)',
full: '0 0.125rem 0.9375rem 0 rgba(0, 0, 0, 0.1)',
'full-light': '0 1px 2px 0 rgba(108, 108, 108, 0.13)',
subHeader: '0 1px 2px 0 rgba(0, 0, 0, 0.06)',
};

/** @type {NonNullable<NonNullable<import('tailwindcss').Config['theme']>['extend']>['fontFamily']} */
export const baseFontFamily = {
ploni: ['ploni'],
};

@zubko
Copy link

zubko commented Sep 29, 2023

I just need colors so far, so I ended up generating them before starting the dev server. I don't change my theme often, so this should be enough for now.

I have this script for now:

import resolveConfig from "tailwindcss/resolveConfig";
import tailwindConfig from "../tailwind.config.js";
import fs from "fs";
import * as prettier from "prettier";

async function main() {
  const fullConfig = resolveConfig(tailwindConfig);

  const contents = `/* Generated file, do not edit */
export const colors = ${JSON.stringify(fullConfig.theme?.colors)}`;

  const filepath = "app/theme.ts";
  const prettierOptions = (await prettier.resolveConfig(process.cwd())) ?? {};
  prettierOptions.filepath = filepath;
  const formatted = await prettier.format(contents, prettierOptions);
  fs.writeFileSync(filepath, formatted);
}

main();

And then in package.json I have:

    "generate-theme": "tsx scripts/generate-theme.ts",
    "dev": "npm run generate-theme && remix dev -c \"npm run dev:serve\"",

Theme file looks pretty good now and TS is happy :)

/* Generated file, do not edit */
export const colors = {
  inherit: "inherit",
  current: "currentColor",
  transparent: "transparent",
  black: "#000",
  white: "#fff",
  slate: {
    "50": "#f8fafc",
    "100": "#f1f5f9",
    "200": "#e2e8f0",
    "300": "#cbd5e1",
    "400": "#94a3b8",
    "500": "#64748b",
    "600": "#475569",
    "700": "#334155",
....

@vimercati-samir
Copy link

vimercati-samir commented Oct 13, 2023

the simplest way that I found to get a typed config (so that I can get the autosuggestion for theme.colors):

tailwind.config.ts

import type { Config } from "tailwindcss";

export default {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {
      colors: {
        primary: "#B8D04F",
        dark: "#1d1d1b",
        light: "#f6f5ec",
      },
    },
  },
  plugins: [],
} satisfies Config;

postcss.config.js

export default {
  plugins: {
    "postcss-import": {},
    "tailwindcss/nesting": {},
    tailwindcss: { config: "./tailwind.config.ts" },
    autoprefixer: {},
    ...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {}),
  },
};

src/imports/theme.ts

import type { Config } from "tailwindcss";
import resolveConfig from "tailwindcss/resolveConfig";
import type { DefaultColors } from "tailwindcss/types/generated/colors";

import tailwindConfig from "../../tailwind.config";

export const { colors } = resolveConfig(tailwindConfig)
  .theme as unknown as Config["theme"] & {
  colors: DefaultColors & typeof tailwindConfig.theme.extend.colors;
};

@baffalop
Copy link
Contributor

For anyone coming to this thread for solutions in the future, now that the config file can be in typescript, using satisfies Config should resolve this issue, eg.

import { Config } from 'tailwindcss/types/config'

export default {
  content: [/* ... */],
  theme: {
     // ...
  },
} satisfies Config

This accomplishes 2 things:

  1. Gives you typechecking so that you know your config values are valid
  2. Allows typescript to infer a more specific type that includes your actual theme values. This should carry through to the resolved config.

Note that without satisfies you could leave out the Config type entirely to get typescript to infer a specific type, but the default inference can get things wrong. For example, I had an error about my font size values, because it inferred that the value '2xs': ['0.625rem', '1rem'] was string[] which isn't assignable to [string, string]. satisfies allows it to narrow down the type of the array to match the expected tuple type.

@vimercati-samir
Copy link

vimercati-samir commented Oct 13, 2023

For anyone coming to this thread for solutions in the future, now that the config file can be in typescript, using satisfies Config should resolve this issue, eg.

import { Config } from 'tailwindcss/types/config'

export default {
  content: [/* ... */],
  theme: {
     // ...
  },
} satisfies Config

This accomplishes 2 things:

  1. Gives you typechecking so that you know your config values are valid
  2. Allows typescript to infer a more specific type that includes your actual theme values. This should carry through to the resolved config.

Note that without satisfies you could leave out the Config type entirely to get typescript to infer a specific type, but the default inference can get things wrong. For example, I had an error about my font size values, because it inferred that the value '2xs': ['0.625rem', '1rem'] was string[] which isn't assignable to [string, string]. satisfies allows it to narrow down the type of the array to match the expected tuple type.

In my experience having the tailwind.config in the ts format and the satisfies was not enough, because I didn't have the autosuggestions for the default colors (it was detecting only the extended colors), It could work as an approach only if you override the entire default theme of tailwind and you write everything inside the config, but if you extend the theme and you still want to get the default configuration suggested it would not work as expected.

@baffalop
Copy link
Contributor

@vimercati-samir Ah that is annoying! We mostly override the theme and do not need to reference anything from theme.extend at runtime, so hadn't run into this.

It sounds like the typing of resolveConfig has some room for improvement to more accurately represent the merging of custom and default that it's responsible for.

@spitza
Copy link

spitza commented Feb 18, 2024

Just doing this for now:

const screens = fullTailwindConfig.theme?.screens;
const mdWidth = (screens as any).md;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants