Quramy / typed-css-modules

Creates .d.ts files from CSS Modules .css files
MIT License
1.03k stars 68 forks source link

Write the .d.ts as `declare module ${pathToFile}` #270

Open SrBrahma opened 8 months ago

SrBrahma commented 8 months ago

The current implementation doesn't allow to import the .css files using absolute import.

To support it, the .d.ts would need to do

declare module 'path/to/your/cssfile.css' {
  const styles: {
    readonly "drawer": string;
    readonly "overlay": string;
    ...
  };
  export = styles;
}

instead of

declare const styles: {
  readonly "drawer": string;
  readonly "overlay": string;
  ...
};
export = styles;
SrBrahma commented 7 months ago

The suggestion also allows all of the types to be in a single file, instead of dozens of files. This surely also fixes the issue with the IDE being slow to update the types when tcm is run.

Also, there could be a flag for the CLI to enforce wildcard imports instead of the default import. This is important for Parcel v2 due to its treeshaking.

The output would be:

declare module 'path/to/your/cssfile.css' {
  export namespace styles {
    const drawer: string;
    const overlay: string;
    // ... other classes
  }
}
ryami333 commented 6 months ago

The suggestion also allows all of the types to be in a single file

Yes, or in multiple files but in a single dedicated directory like /types. We would really like this feature ❤️

stevenpetryk commented 5 months ago

As far as I know this simply doesn't work in TypeScript. Ambient modules cannot declare the types for specific files, only general imports. So for example,

declare module "test.module.css" {
  const styles: {
    readonly foo: string
    readonly background: string
  }
  export = styles
}

Will only add typings for:

import styles from "test.module.css";

Not the much more common:

import styles from "./test.module.css";

And you are not allowed to use relative or absolute paths when declaring such modules:

ryami333 commented 5 months ago

@stevenpetryk most of what you said is correct - except one little detail that actually makes a workaround possible.

you are not allowed to use relative or absolute paths

You're right that relative paths don't work, but absolute paths do. Given that, consider a project like this:

Screenshot 2024-03-12 at 19 32 56

With tsconfig's path aliases, you can add an import alias for any folder, including the root folder. You could alias the src/styles folder as @styles with this Tsconfig:

{
  // …
  "compilerOptions": {
    // …
    "paths": {
      "@styles/*": ["./src/styles/*"]
    }
  }
}

Now we can do this (although you'd still get a Cannot find module '@styles/foo.module.css' or its corresponding type declarations. error for now):

// src/index.ts

import fooStyles from '@styles/foo.module.css`;

Now, you can add a definition file anywhere in the project, like src/global.d.ts and declare modules using the aliased path:

// global.d.ts

declare module "@styles/foo.module.css" {
  const stylesheet: { hello: "world" }; // <- to be generated by TCM
  export default stylesheet;
}

declare module "@styles/bar.module.css" {
  const stylesheet: any; // <- to be generated by TCM
  export default stylesheet;
}

And now the foo import in src/index.ts will be typed as { hello: "world" }.

Screenshot 2024-03-12 at 19 41 22

Summary

tcm could generate a single styles.d.ts file, as long as it accepted alias parameters, eg.

npx tcm --ambient --ambient-alias="@styles" --ambient-alias="src/styles"

Then we can use Typescript's path aliases until relative ambient module declarations become a thing. This feature could even be used as prior art in a Typescript feature request.

stevenpetryk commented 5 months ago

Hm, yeah I suppose that works. Could be worth it if you're willing to rewrite all your existing CSS imports.