Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSS prop incompatible with typescript #693

Closed
cdock1029 opened this issue May 29, 2018 · 34 comments
Closed

CSS prop incompatible with typescript #693

cdock1029 opened this issue May 29, 2018 · 34 comments

Comments

@cdock1029
Copy link

  • emotion version: 9.1.3
  • react version: 16.4.0
  • react-emotion: 9.1.3
  • react-app-rewire-emotion: 4.0.0
  • babel-plugin-emotion: 9.1.2
  • @types/react: 16.3.4
  • @types/react-dom: 16.0.5

Relevant code.

class App extends Component {
  render() {
    return (
      <div
        className="App"
        css={`
          color: pink;
        `}
      >
        <p>hello world</p>
      </div>
    )
  }
}

What you did:

try to compile App.tsx

What happened:

Compilation error:

TS2339: Property 'css' does not exist on type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'.
No lint errors found
Version: typescript 2.8.3, tslint 5.10.0

Problem description:

Typescript doesn't understand css prop.

@Ailrun
Copy link
Member

Ailrun commented Jun 8, 2018

@cdock1029 Is this still relavant with 9.2.2?

@cdock1029
Copy link
Author

No this seems to be resolved with current version, thanks.

@Ailrun
Copy link
Member

Ailrun commented Jun 11, 2018

Close this based on #693 (comment)

@Ailrun Ailrun closed this as completed Jun 11, 2018
@aaronjensen
Copy link
Contributor

@Ailrun afaict this seems to not work w/ preact-emotion (thanks for typing that btw!)

@Ailrun
Copy link
Member

Ailrun commented Jun 17, 2018

@aaronjensen Oh, really? I need to check Preact typing again.

emmatown pushed a commit that referenced this issue Jun 26, 2018
<!--
Thanks for your interest in the project. I appreciate bugs filed and PRs submitted!

Please make sure that you are familiar with and follow the Code of Conduct for
this project (found in the CODE_OF_CONDUCT.md file).

Also, please make sure you're familiar with and follow the instructions in the
contributing guidelines (found in the CONTRIBUTING.md file).

If you're new to contributing to open source projects, you might find this free
video course helpful: http://kcd.im/pull-request

Please fill out the information below to expedite the review and (hopefully)
merge of your pull request!
-->

<!-- What changes are being made? (What feature/bug is being fixed here?) -->
**What**:
This adds typescript tests for the css prop for both React and Preact. The
Preact tests are currently failing, so they'd need to be fixed before this can
be merged. See #693 (comment)

<!-- Why are these changes necessary? -->
**Why**:

The tests weren't there before and the css prop only worked with React.

<!-- How were these changes implemented? -->
**How**:

I'm not sure how to answer this question.

<!-- Have you done all of these things?  -->
**Checklist**:
<!-- add "N/A" to the end of each line that's irrelevant to your changes -->
<!-- to check an item, place an "x" in the box like so: "- [x] Documentation" -->
- [ ] Documentation N/A
- [x] Tests
- [x] Code complete

<!-- feel free to add additional comments -->

<!-- Please add a `Tag:` prefixed label from the labels so that this PR shows up in the changelog -->
@emattias
Copy link
Contributor

I had to add react-emotion to the types array in my tsconfig.json: (it works with emotion and create-emotion aswell)

"types": ["node", "react-emotion"]

for the addition of the css prop/attribute to the HTMLAttributes interface to be included: https://github.com/emotion-js/emotion/blob/v9.2.8/packages/create-emotion/types/index.d.ts#L94-L98

@karol-majewski
Copy link
Contributor

This definition now lives in the core package — so the entry in tsconfig.json should say "@emotion/core" instead of "react-emotion".

declare module 'react' {
interface DOMAttributes<T> {
css?: InterpolationWithTheme<any>
}
}

@kentcdodds
Copy link
Contributor

Huh, I can't seem to get this to work in a fresh codebase. Here's a small reproduction:

https://github.com/kentcdodds/ts-and-emotion-not-working

Running npx tsc I get:

example.tsx:6:6 - error TS2322: Type '{ children: string; css: { marginTop: number; }; }' is not assignable to type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'.
  Property 'css' does not exist on type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'.

6     <div css={{marginTop: 10}}>css does not</div>
       ~~~


Found 1 error.

You can look at the repo, but here are the details to save you a click:

example.tsx

import * as React from 'react'

export default () => (
  <>
    <div style={{marginTop: 10}}>style works</div>
    <div css={{marginTop: 10}}>css does not</div>
  </>
)

tsconfig.json

{
  "types": ["node", "@emotion/core"],
  "compilerOptions": {
    "noEmit": true,
    "jsx": "react"
  },
  "include": ["example.tsx"]
}

I've got react, @emotion/core, and @emotion/styled all installed on this repo.

Any ideas or things that I'm missing would be appreciated! I'm trying to get gatsby + gatsby-plugin-emotion + gatsby-plugin-typescript to work nicely together :)

@Jessidhia
Copy link

Jessidhia commented Dec 18, 2018

You're augmenting the wrong thing (rather, the compiler's JSX support is weird in a lot of ways and the only sensible thing to augment that works with both intrinsic and non-intrinsic components is this).

The augmentation you are looking for is 'react''s Attributes.

declare module 'react' {
  interface Attributes {
    css?: InterpolationWithTheme<any>
  }
}

@kentcdodds
Copy link
Contributor

Thanks @Kovensky! So you're saying that this should be Attributes not DOMAttributes?

I'll double check locally then I can make a PR for that. Thank you!

@Jessidhia
Copy link

Jessidhia commented Dec 18, 2018

Yeah, and you can leave the namespace JSX alone.

This is how I did it for styled-components, where we made the css prop's declaration opt-in:

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/styled-components/cssprop.d.ts#L4-L19

@kentcdodds
Copy link
Contributor

Hmmm... That doesn't seem to be working for me. I'm trying to make the change to my local node_modules and it's still giving me the same error.

@BarryThePenguin
Copy link

Do you need to specify the emotion jsx factory to use the css prop in emotion@10?

This compiles when I run npx tsc

/** @jsx jsx */
import * as React from 'react'
import { jsx } from '@emotion/core'

export default () => (
  <React.Fragment>
    <div style={{marginTop: 10}}>style works</div>
    <div css={{marginTop: 10}}>css does not</div>
  </React.Fragment>
)

@Jessidhia
Copy link

@kentcdodds Right, 'react' is only augmented if '@emotion/core' is imported at all. (Which is technically incorrect for emotion as you also need to use the pragma, but it's not currently possible to represent that in typescript.)

@kentcdodds
Copy link
Contributor

Thanks @BarryThePenguin! That does work but:

  1. I have a babel plugin configuration which allows me to not have to import emotion's jsx function/specify the pragma
  2. I'd prefer to be able to use the <></> fragment syntax.

Is there any way we can make this work while maintaining those two?

@kentcdodds
Copy link
Contributor

Thanks for the help friends :)

Right, 'react' is only augmented if '@emotion/core' is imported at all.

I thought that we could force these typings to be included if we add the types configuration as I have in the the tsconfig.json. I'm pretty new to TS, so I may be misunderstanding something. Could I make custom global definitions for my project that are included to do this?

@Jessidhia
Copy link

Jessidhia commented Dec 18, 2018

@kentcdodds Oh, I just realized, your "types" are in the wrong object. It's not top-level, it should be inside "compilerOptions".

(also, I'd suggest using esModuleInterop and import React from 'react' instead, because 'react' is not a real ES module)

@kentcdodds
Copy link
Contributor

Fantastic, thank you so much @Kovensky! That solved it for me <3

So I'm a bit put off by this solution because of this note in the docs:

This tsconfig.json file will only include ./node_modules/@types/node, ./node_modules/@types/lodash and ./node_modules/@types/express. Other packages under node_modules/@types/* will not be included.

I feel like it would be nice to not have to keep this list updated and just auto-include all the node_modules/@types definitions. Am I unnecessarily concerned?

@Jessidhia
Copy link

Jessidhia commented Dec 18, 2018

That would automatically make any @types package you install (directly or indirectly) that has global declarations make them available to your code.

I personally avoid it (even giving types: [] on some packages) but if you want to keep the behavior, then you can remove the types array and just add a /// <reference types="@emotion/core"/> or import {} from '@emotion/core' anywhere in your project (even in a .d.ts file that is caught by your includes). TypeScript elides import {} from as being a type-only import (as in, you made no use of any concrete values from the import).

@kentcdodds
Copy link
Contributor

Interesting. Thank you for sharing that @Kovensky! You've been very helpful to me tonight (both here and on twitter! 🙏)

@tkh44
Copy link
Member

tkh44 commented Dec 18, 2018

Thanks for helping out @Kovensky 👍

@ralexand56
Copy link

Yeah, and you can leave the namespace JSX alone.

This is how I did it for styled-components, where we made the css prop's declaration opt-in:

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/styled-components/cssprop.d.ts#L4-L19

I don't understand. This doesn't work with styled components either. What do you mean by opt in?

@felixjung
Copy link
Contributor

If anyone is having trouble with getting this to work, nuking node_modules and my yarn.lock resolved all my issues with the css prop. Found this here: DefinitelyTyped/DefinitelyTyped#21242.

Not ideal, but it does the job.

@barrymay
Copy link

barrymay commented Jul 3, 2019

I was trying to figure out this issue in relation to a simple app created with https://github.com/zeit/next.js (awesome package btw!)

(note this wasn't failing the build, but just complaining in VSCode)

After much fumbling it seemed somehow to a separate install of @types/react under @types/next/node_modules. Since @types/react was already under my main folder's node_modules, I didn't feel adding it was required. But by hovering over a declare module 'react' reference, I saw it was pointing to this underlying node_modules folder.

TLDR: By doing

yarn add @types/react

it fixed the problem, seemingly so that @types/next could just point to the version already installed by @types/react in package.json (educated guess), so it didn't add any additional install

Couldn't reproduce after this, but hope this helps someone ;)

@kripod
Copy link
Contributor

kripod commented Oct 20, 2019

The TypeScript workflow could be simplified by providing a @types/emotion-core package through DefinitelyTyped, containing type augmentations without having to override types in tsconfig.json. @emotion/core could add @types/emotion-core as a dependency for fluid, zero-config developer experience.

@radihuq
Copy link

radihuq commented Feb 14, 2020

I'm working with a monorepo built using yerna and @barrymay 's answer was the solution for me

@mavlikwowa
Copy link

mavlikwowa commented Jan 2, 2021

If you have errors with types "type" and "as" you should redefine this types before intert these in styled components

/* Button component */
const Button: React.FC<ButtonProps> = ({
  isLoading,
  children,
  // should redefine this types because React.HTMLProps<HTMLButtonElement> has incompatible similar types
  type = 'button',
  as = undefined,
  ...props
}: ButtonProps, ...others) => {
  return (
    <StyledButton {...props} {...others}>
      {isLoading ? 'Loading...' : children}
    </StyledButton>
  );
};

export default Button;

@sekoyo
Copy link

sekoyo commented Jan 15, 2021

For those poor travellers using Emotion 11 the answer is here: #1249 (comment)

@Andarist
Copy link
Member

If you are already on TS 4.1 you should follow the first recommendation here: https://emotion.sh/docs/typescript#css-prop

It's all in the docs...

@sekoyo
Copy link

sekoyo commented Jan 15, 2021

If you are already on TS 4.1 you should follow the first recommendation here: https://emotion.sh/docs/typescript#css-prop

It's all in the docs...

Thanks I tried that and it didn't work, but now I realise it requires a VSCode restart to take effect there.

@Andarist
Copy link
Member

I'm pretty sure that I didn't have to restart VScode but maybe you have VScode configured differently than me and some caching has prevented it from reloading the TypeScript server/program.

@utenma
Copy link

utenma commented Mar 25, 2021

React 17 with Typescript 4.2 and Emotion.js 11.1 setup

Just add the following line to react-app-env.d.ts as stated in https://emotion.sh/docs/emotion-11
/// <reference types="@emotion/react/types/css-prop" />

and import with

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

in tsconfig.json make shure to have "jsx": "react-jsx"

@neaumusic
Copy link

I had a similar issue spreading the rest of my props from HTMLAttributes<HTMLDivElement>, and I realized the issue was actually that there's a css prop passed in by default, not sure if it's from the theme or a global, but casting that as cssProps or an unused _cssProps seems to have solved the issue. I'm not sure how to spread cssProps back into the custom css attribute of the div, but if you have a theme setup this may be easy to test.

export interface MyComponentProps extends React.HTMLAttributes<HTMLDivElement> {
    someProp: string;
};

export const MyComponent: React.FC<MyComponentProps> = ({
  css: cssProp,
  someProp,
}) => (
  <div
    css={css`
      background-color: "black",
      ...cssProp // not sure about this line
    `}
  />
);

@srmagura
Copy link
Member

srmagura commented Apr 6, 2022

@neaumusic css prop is passed down to the component as a className, so MyComponent will never have access to the css prop directly. I think this would work:

export interface MyComponentProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'css'> {
    someProp: string;
};

export const MyComponent: React.FC<MyComponentProps> = ({
  someProp, className
}) => (
  <div
    css={css`
      background-color: "black"
    `}
    className={className}
  />
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests