0

I want to use not a dumb mock for CSS modules but real types so I use webpack plugin typings-for-css-modules-loader

for simple css

.foo { color: red; }
.bar { color: green; }

it can generate example.css.d.ts declaration

export interface ILocals {
  'foo': string;
  'bar': string;
}
export interface IExampleCss {
  locals: ILocals;
  'foo': string;
  'bar': string;
}

declare const styles: IExampleCss

export default styles;

so in my code I able to use it like so

import css from './example.css';

const { locals } = css;

<div className={locals.foo}>Red</div>
<div className={css.bar}>Green</div>

and VSCode knows that locals.foo and css.bar members

I try to write a function which is able to get any css definitions in typescript as an input parameter

interface IStringHash {
    [key: string]: string;
}
interface ICSSLocals {
    locals: IStringHash;
    [key: string]: string | IStringHash;
}

interface ISomeInterface {
    renderComponent: (css: ICSSLocals): React.ReactNode
}

const myFunction = (css: IExampleCss) => {
    return (
        <div key="1" className={css.locals.foo}>Red</div>
        <div key="2" className={css.bar}>Green</div>
    )
}

const MyRenderer: ISomeInterface = {
    renderComponent: myFunction
                     ^^^^^^^^^^
};

I get the error:

[ts] Type 'IExampleCss' is not assignable to type 'ICSSLocals'. Type 'IExampleCss' is not assignable to type 'ICSSLocals'. Types of property 'locals' are incompatible. Type 'ILocals' is not assignable to type 'IStringHash'. Index signature is missing in type 'ILocals'.

What is wrong?

I've wrote a relevant sample in typescript playground

// Declaration with generics
interface IStringHash {
    [key: string]: string;
}
interface ICSSModule {
    locals: IStringHash;
    [key: string]: string | IStringHash;
}

interface returnObject {
    a: string,
    b: string
}

interface ISomeInterface {
    renderComponent: (css: ICSSModule) => returnObject
}

// usage with certain realisation
export interface ILocals {
  'foo': string;
  'bar': string;
}
export interface IExampleCss {
  locals: ILocals;
  'foo': string;
  'bar': string;
}

const myFunction = (css: IExampleCss): returnObject => {
    return {
        a: css.locals.foo,
        b: css.bar
    }
}

const MyRenderer: ISomeInterface = {
    renderComponent: myFunction
      ^^^^^^^
};

here is the error:

[ts] Type '(css: IExampleCss) => returnObject' is not assignable to type '(css: ICSSModule) => returnObject'. Types of parameters 'css' and 'css' are incompatible. Type 'ICSSModule' is not assignable to type 'IExampleCss'. Property ''foo'' is missing in type 'ICSSModule'. (property) ISomeInterface.renderComponent: (css: ICSSModule) => returnObject

6
  • Can you give more info what are you doing with myFunction ? Taking 'any css declaration' seems not much better than just using any type. Commented Sep 29, 2018 at 6:15
  • @Ski I've fixed function above Commented Sep 29, 2018 at 11:44
  • @Ski I need type suggestions an show error if there are Commented Sep 29, 2018 at 11:56
  • I'm confused: if myFunction accesses css.locals.foo and css.bar, then it will only work for CSS modules that have classes named foo and bar. So why are you trying to declare myFunction as taking any CSS module? Commented Sep 30, 2018 at 2:39
  • @MattMcCutchen I use some loaders for servrer & browser usage styles - so there it's not so important Commented Oct 1, 2018 at 12:19

2 Answers 2

1

Good practice is to only require what you need of the interface and not more. So looks like bar should not be in ILocals and foo should not be in IExampleCss

export interface ILocals {
  'foo': string;   
//  'bar': string; <--
}
export interface IExampleCss {
  locals: ILocals;
  // 'foo': string; <--
  'bar': string;
}

const myFunction = (css: IExampleCss): returnObject => {
    return {
        a: css.locals.foo,
        b: css.bar
    }
}

Sidenote: for simple cases you don't have to declare return type - it will be inferred. And if you need the type of function return value, you can use ReturnType<myFunction>.

Sign up to request clarification or add additional context in comments.

2 Comments

css.locals.foo is used for sample to show that any item can be accessed directly or through locals
Sorry mixed this up, foo should stay, bar should go
0

try out this webpack loader: React-css-components loader

configuration example with nextjs

const nextConfig = {
  webpack: (config, { dev, isServer }) => {
    const rccLoaderRule = {
      test: /\.module\.scss$/,
      use: [
        {
          loader: 'rcc-loader',
          options: {
            enabled: !!dev && isServer,
            exportStyleOnly: true
          }
        }
      ]
    }

    config.module.rules = config.module.rules ?? []
    config.module.rules.push(rccLoaderRule)

    return config
  }
}

Comments

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.