css-modules-typescript-loader icon indicating copy to clipboard operation
css-modules-typescript-loader copied to clipboard

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

Open silbinarywolf opened this issue 6 years ago • 5 comments
trafficstars

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

  • noImplicitAny: false

silbinarywolf avatar Sep 30 '19 01:09 silbinarywolf

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

  • const enum can have problems with isolatedModules
    • https://github.com/babel/babel/issues/8741

silbinarywolf avatar Sep 30 '19 01:09 silbinarywolf

@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 avatar Sep 30 '19 01:09 silbinarywolf

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 avatar Oct 17 '19 06:10 silbinarywolf

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 avatar Oct 18 '19 01:10 silbinarywolf

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.

silbinarywolf avatar Oct 18 '19 04:10 silbinarywolf