seek-oss / css-modules-typescript-loader

Webpack loader to create TypeScript declarations for CSS Modules
MIT License
198 stars 27 forks source link

CSS Selectors using hyphens aren't compile-time checked due to square bracket access #26

Open silbinarywolf opened 5 years ago

silbinarywolf commented 5 years ago

Problem I discovered when using this that if you write CSS selectors like this .my-selector, with a hyphen, when you import the CSS and access it with square bracket syntax, you lose compile-time checking on whether the selector / property exists or not.

.contact-details {
  color: #000;
}
.ContactDetails {
  color: #000;
}
// No compile-time error!
styles["contact-dAetails"]

// You get a compile-time error!
styles.ContactDAetails

Proposed solution(s) a) Figure out if TypeScript has the ability to enforce checking when accessing something with square brackets. b) If "a" is not possible, at least mention in documentation that this is a problem!

Notable TSConfig setup

silbinarywolf commented 5 years ago

The following alternate generated syntax will give compile-time errors with hyphen values.

// This file is automatically generated.
// Please do not change this file!
const enum CssExports {
    'item' = 'item',
    'list' = 'list',
    'root' = 'root',
    'text' = 'text',
    'text--small' = 'text--small',
    'title' = 'title',
    'title--small' = 'title--small',
}
declare const cssExports: typeof CssExports;
export = cssExports;
[tsl] ERROR in C:\javascript\my-component.tsx(28,33)
      TS2339: Property 'titlae--small' does not exist on type 'typeof CssExports'.

Potential problems

silbinarywolf commented 5 years ago

@jahredhope I'm happy to make a PR + tests if you guys are willing to accept it. If I put in the effort will this get merged?

silbinarywolf commented 5 years ago

I explored this option and it can fail in cases where users simply passed the entire styles variable to another value.

ie. we're utilizing this in a project using Aurelia (with an aim to move slowly to React), so for our Aurelia components, we can't expose the CSS modules object to the templater of that particular framework.

@autoinject
@useView(PLATFORM.moduleName('./my-component.html'))
export class MyComponent  {
    private readonly styles = styles;
}

I'm going to close this.

silbinarywolf commented 5 years ago

Nevermind, I think I have a nice solution / patch to this problem. We just define an index signature that returns void.

For example:

// This file is automatically generated.
// Please do not change this file!
interface CssExports {
  [key: string]: void; // <- this makes typos give you an error
  'orb': string;
  'orb-active-user': string;
  'orb-exited-user': string;
  'orb-list': string;
}
declare const cssExports: CssExports;
export = cssExports;
ERROR in components\entity-search\entity-search.tsx
components/entity-search/entity-search.tsx
[tsl] ERROR in components\entity-search\entity-search.tsx(945,7)
      TS2322: Type 'void' is not assignable to type 'string'.
silbinarywolf commented 5 years ago

This approach has problems as pointed out by @mattsjones in his comment: https://github.com/seek-oss/css-modules-typescript-loader/pull/29#issuecomment-543501062

Needs further investigation.