Skip to content

Commit 2790dc9

Browse files
authored
Update/correct TypeScript docs to reflect current best practice (#2598)
* Update/correct TypeScript docs to reflect current best practice #2596 * Clean up typescript.mdx per Andarist's suggestions * Add back deleted section to typescript.mdx
1 parent e442f44 commit 2790dc9

File tree

1 file changed

+35
-57
lines changed

1 file changed

+35
-57
lines changed

docs/typescript.mdx

Lines changed: 35 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,19 @@
22
title: 'TypeScript'
33
---
44

5-
Emotion includes TypeScript definitions for `@emotion/react` and `@emotion/styled`. These definitions also infer types for css properties with the object syntax, HTML/SVG tag names, and prop types.
5+
Emotion includes TypeScript definitions for `@emotion/react` and `@emotion/styled`. These definitions infer types for css properties with the object syntax, HTML/SVG tag names, and prop types.
66

77
## @emotion/react
88

9+
The easiest way to use the css prop with TypeScript is with the new JSX transform and the `jsxImportSource` TSConfig option (available since TS 4.1). For this approach, your TSConfig `compilerOptions` should contain
10+
11+
```json
12+
"jsx": "react-jsx",
13+
"jsxImportSource": "@emotion/react"
14+
```
15+
16+
For most users, this is all the setup that is required. You can now define styles using the object syntax or template literal syntax and pass them to your components via the `css` prop.
17+
918
```tsx
1019
import { css } from '@emotion/react'
1120

@@ -22,7 +31,7 @@ const subtitleStyle = css`
2231
`
2332
```
2433

25-
TypeScript checks css properties with the object style syntax using [csstype](https://www.npmjs.com/package/csstype) package, so following code will emit errors.
34+
Object styles are recommended since they are type checked with the help of the [csstype](https://www.npmjs.com/package/csstype) package. For example, the following code will emit an error.
2635

2736
```tsx
2837
import { css } from '@emotion/react';
@@ -35,47 +44,41 @@ const titleStyle = css({
3544
});
3645
```
3746

38-
To make the css prop work with pure TypeScript (without babel plugin) you need to add `/** @jsx jsx */` at the top of every file that is using the css prop:
47+
When using our JSX factory, TypeScript only allows the `css` prop on components that accept a `className` prop. This is because `@emotion/react` resolves the value of the `css` prop to a class name and then passes this class name down to the rendered component.
48+
49+
### With the Babel plugin
50+
51+
[`@emotion/babel-plugin`](/docs/babel) is completely optional for TypeScript users. If you are not already using Babel, you probably shouldn't add it to your build tooling unless you truly need one of the features offered by `@emotion/babel-plugin`. On the other hand, there's no reason not to use `@emotion/babel-plugin` if you are already using Babel to transpile your TypeScript code.
52+
53+
### With the old JSX transform
54+
55+
If you are unable to upgrade to the `react-jsx` transform, you will need to specify the JSX factory at the top of every file:
3956

4057
```tsx
4158
/** @jsx jsx */
4259
import { jsx } from '@emotion/react'
43-
44-
<div css={{ background: 'black' }} />
4560
```
4661

47-
As a result you may be not able to use react fragment shorthand syntax - `<></>`, but still you can use `<Fragment></Fragment>`.
48-
This is a limitation of the TypeScript compiler not being able to independently specify jsx pragma and jsxFrag pragma.
62+
As a result, you may be not able to use the shorthand syntax `<></>` for React fragments, but you can still use `<Fragment></Fragment>`. This is a limitation of the TypeScript compiler not being able to independently specify jsx pragma and jsxFrag pragma.
4963

5064
You can still use the css helper and pass the className yourself (ensure you are importing from the `@emotion/css` package, not `@emotion/react`).
5165

5266
```tsx
5367
import { css } from '@emotion/css'
5468

55-
<div className={css({ background: 'black' })} />
69+
const el = <div className={css({ background: 'black' })} />
5670
```
5771

58-
### `css` prop
59-
60-
When using our JSX factories the support for `css` prop is being added only for components that accepts `className` prop as they take provided `css` prop, resolves it and pass the generated `className` to the rendered component.
61-
62-
If using the automatic runtime you should just add this to your `tsconfig.json` to let TypeScript know where it should look for the `JSX` namespace:
63-
```json
64-
{
65-
"compilerOptions": {
66-
"jsxImportSource": "@emotion/react"
67-
}
68-
}
69-
```
70-
71-
The same `JSX` namespace is resolved if you are still using the classic runtime through the `@jsx` pragma. However, it's not possible to leverage `css` prop support being added conditionally based on a type of rendered component when one is not using our jsx pragma or the automatic runtime. For those cases when people use our pragma implicitly (for example when using our `@emotion/babel-preset-css-prop`) we have a special file that can be imported once to add support for the `css` prop globally, for all components. Use it like this:
72+
It's not possible to leverage `css` prop support being added conditionally based on the type of a rendered component when not using our jsx pragma or the `react-jsx` transform. If you use our pragma implicitly (for example when using our `@emotion/babel-preset-css-prop`) we have a special file that can be imported once to add support for the `css` prop globally, for all components. Use it like this:
7273

7374
```ts
7475
/// <reference types="@emotion/react/types/css-prop" />
7576
```
7677

7778
## @emotion/styled
7879

80+
`@emotion/styled` works with TypeScript without any additional configuration.
81+
7982
### HTML/SVG elements
8083

8184
```tsx
@@ -170,13 +173,12 @@ interface ComponentProps {
170173
label: string
171174
}
172175

173-
const Component: FC<ComponentProps> = ({
174-
label,
175-
className
176-
}) => <div className={className}>{label}</div>
176+
const Component: FC<ComponentProps> = ({ label, className }) => (
177+
<div className={className}>{label}</div>
178+
)
177179

178180
const StyledComponent0 = styled(Component)`
179-
color: ${props => props.label === 'Important' ? 'red' : 'green'};
181+
color: ${props => (props.label === 'Important' ? 'red' : 'green')};
180182
`
181183

182184
const StyledComponent1 = styled(Component)({
@@ -195,12 +197,12 @@ const App = () => (
195197

196198
Sometimes you want to wrap an existing component and override the type of a prop. Emotion allows you to specify a `shouldForwardProp` hook to filter properties which should be passed to the wrapped component.
197199

198-
If you make `shouldForwardProp` a [type guard](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) then only the props from the type guard will be exposed.
200+
If you make `shouldForwardProp` a [type guard](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) then only the props from the type guard will be exposed.
199201

200202
For example:
201203

202-
``` ts
203-
const Original: React.FC<{ prop1: string, prop2: string }> = () => null
204+
```ts
205+
const Original: React.FC<{ prop1: string; prop2: string }> = () => null
204206

205207
interface StyledOriginalExtraProps {
206208
// This prop would conflict with the `prop2` on Original
@@ -286,7 +288,7 @@ declare module '@emotion/react' {
286288

287289
// You are also able to use a 3rd party theme this way:
288290
import '@emotion/react'
289-
import { LibTheme } from 'some-lib'
291+
import { LibTheme } from 'some-lib'
290292

291293
declare module '@emotion/react' {
292294
export interface Theme extends LibTheme {}
@@ -307,38 +309,14 @@ const Button = styled('button')`
307309
export default Button
308310
```
309311

310-
If you were previously relying on `theme` being an `any` type, you have to restore compatibility with:
312+
If you were previously relying on `theme` being an `any` type, you can restore compatibility with:
311313

312314
_emotion.d.ts_
313315

314316
```ts
315317
import '@emotion/react'
316318

317319
declare module '@emotion/react' {
318-
export interface Theme extends Record<string, any> {}
320+
export interface Theme extends Record<string, any> {}
319321
}
320322
```
321-
322-
### TypeScript < 2.9
323-
324-
For Typescript <2.9, the generic type version only works with object styles due to https://github.com/Microsoft/TypeScript/issues/11947.
325-
326-
You can work around this by specifying the prop types in your style callback:
327-
328-
``` ts
329-
const StyledComponent0 = styled(Component)`
330-
color: red;
331-
background: ${(props: StyledComponentProps) =>
332-
props.bgColor};
333-
`
334-
```
335-
336-
NOTE: This approach you will have to perform the intersection with the component props yourself to get at the component props
337-
338-
``` ts
339-
const StyledComponent0 = styled(Component)`
340-
color: red;
341-
background: ${(props: StyledComponentProps & ComponentProps) =>
342-
props.bgColor};
343-
`
344-
```

0 commit comments

Comments
 (0)