3

I'm using Tailwind in my project, which causes the className attributes for DOM elements to be rather long and sometimes even hold logic. To help with organisation, I'm using the clsx utility:

const Foo: React.FC<{ bar: boolean, baz: number }> = ({ bar, baz }) => {
  return <div className={clsx("…", bar ? "…" : "…", baz < 10 && "…")} />;
  //                     ----
  //                     clsx: (...inputs: ClassValue[]) => string
};

By using the Babel auto-import plugin and declaring clsx to be a function in the global scope, I save a few precious keystrokes not importing the function in every file, but I want to save a few more by using a Babel plugin like transform-jsx-classnames, which will apply clsx to the className values automatically. TypeScript, however, doesn't know anything about the Babel plugin, so it is naturally unhappy with this:

const Foo: React.FC<{ bar: boolean, baz: number }> = ({ bar, baz }) => {
  return <div className={["…", bar ? "…" : "…", baz < 10 && "…"]} />;
  //          ~~~~~~~~
  //          React.HTMLAttributes<HTMLDivElement>.className?: string | undefined
  //          Type '(string | false)[]' is not assignable to type 'string'.
};

It looks like I'll want to override the React.HTMLAttributes.className type to something like any[], but I'm not sure how to do that.

I have my own project.d.ts file for importing the types of the auto-imported utilities to the global scope. I tried extending the React types there with only partial success — declaring custom attributes works, but I'm still unable to alter the existing className attribute:

import _clsx from "clsx";

declare global {
  const FOO: boolean;
  const BAR: boolean;
  const clsx: typeof _clsx;
  declare namespace foobar {}
}

declare module "react" {
  interface HTMLAttributes {
    className?: any[]; // Doesn't work
    customAttr: number; // Works!
  }
}
const Foo = () => {
  return <div />;
  //     ~~~~~~~
  //     Type '{}' is not assignable to type
  //     'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'.
  //
  //     Property 'customAttr' is missing in type '{}' but required in type
  //     'HTMLAttributes<HTMLDivElement>'.
  //     ts(2322)
  //
  //     project.d.ts(…): 'customAttr' is declared here.
};
const Bar = () => {
  return <div className={["foo"]} customAttr={0} />;
  //          ~~~~~~~~~
  //          HTMLAttributes<HTMLDivElement>.className?: string | undefined
  //          Type 'string[]' is not assignable to type 'string'.
  //          ts(2322)
  //
  //          @types/react index.d.ts(…):
  //          The expected type comes from property 'className' 
  //          which is declared here on type 'DetailedHTMLProps<…>'
};

How do I get TypeScript use my className: any[] type instead of React's className?: string | undefined?

1

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.