Emotion: Declaring Theme interface for @emotion/react module breaks typings of returned values of styled and withComponent

Created on 12 Feb 2020  路  10Comments  路  Source: emotion-js/emotion

Current behavior:

When I add

declare module '@emotion/react' {
  export {Theme} from 'somewhere';
}

typings for theme prop is correctly set, but whenever type of styled(Element) becomes StyledComponent<any, {}>. If I remove the above declaration, the type of styled(Element) becomes

StyledComponent<Props & etc>

which is correct.

Also import {ThemeProvider} from '@emotion/react'; doesn't work unless I add export {ThemeProvider} from '@emotion/react'; to the declaration.

To reproduce:

Add this declaration to your code:

declare module '@emotion/react' {
  export {Theme} from 'somewhere';
}

Expected behavior:

Providing theme typing don't break other stuff.

Environment information:

  • react 16.12.0
  • emotion version: 11.0.0-next.10
documentation

Most helpful comment

Interesting - I can observe the same as you using the provided repro. I don't quite understand why this happens, I was under the impression that this would just work without importing our package inside this augmenting file.

I've prepared a PR to the docs with this included https://github.com/emotion-js/emotion/pull/1803 . Thank you for investigating this!

All 10 comments

Also happens in 11.0.0-next.11

Please provide a runnable repro case. I have very limited time lately and setting this up myself is, unfortunately, a no go - especially that I might not be able to reproduce your setting exactly 1to1 and thus might fail to repro it for some reason.

Sure, I'll prepare it soon

@Andarist Please take a look at this: https://github.com/sassanh/emotion-issue-1757-reproduction

There are two issues in this repository:

  1. Line 29 in src/App.tsx clor is a typo but since theme is of type any, Typescript doesn't report any issues. theme should be of type {color: string} because of the content of src/react-app-env.d.ts.
  2. Line 5 in src/App.tsx ThemeProvider is not exported from @emotion/react if I remove the content of src/react-app/env.d.ts it'll get fixed. Also if I add
  export {ThemeProvider} from '@emotion/react';

to that file, it'll get fixed as well.

Thanks, I will take a look at this - it might take a few days though. You could also try to investigate this on your own (or with my help) and fix the issue sooner by providing a PR with a fix.

By tracing the issue in styled-components to see how it's resolved there, I found a workaround specially by reading https://github.com/styled-components/styled-components-website/issues/447

After some trial and error I found that declaring an emotion.d.ts (or whatever) file which contains:

import '@emotion/react';

declare module '@emotion/react' {
  export interface Theme {
    color: string;
  }
}

resolves both issues, importing @emotion/react on top of the file fixes the problem of ThemeProvider (and other exports of emotion) not being available.

And to fix theme being of type any you have to declare the theme completely in module declaration instead of declaring it in another file and import and re-exporting it here.

@sassanh Worked like a charm. Thank you!

Note that I was able to do this, reusing my actual Theme typings :

import "@emotion/react";

import { Theme as EmotionTheme } from "./styles/theme";

declare module "@emotion/react" {
  export interface Theme extends EmotionTheme {}
}

Interesting - I can observe the same as you using the provided repro. I don't quite understand why this happens, I was under the impression that this would just work without importing our package inside this augmenting file.

I've prepared a PR to the docs with this included https://github.com/emotion-js/emotion/pull/1803 . Thank you for investigating this!

TypeScript files can be in one of two modes: ambient (global) or module. This almost ended up being how Node would tell the difference between script and module before they went the .mjs route instead.

When a .d.ts file is in ambient context, declare module replaces the module. When in a module, it augments it. This is documented... somewhere in the TS docs, but it sure isn't clear.

Anyway, by adding any import or export statement at root level, you can switch to module context. So, the following works, too.

export {};

declare module '@emotion/react' {
  export interface Theme {
    color: string;
  }
}

Closing this as the appropriate change has been added to the docs some time ago already.

@threehams Thanks for the additional information, this has been quite helpful - TIL.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mitchellhamilton picture mitchellhamilton  路  3Comments

hamlim picture hamlim  路  3Comments

rockmandash picture rockmandash  路  3Comments

artooras picture artooras  路  3Comments

mattfysh picture mattfysh  路  3Comments