Skip to content

Commit 39bae76

Browse files
authored
Add Documentation for Using React-JSS with TS (#1467)
* Created a new page for using React-JSS with TypeScript. * Added documentation for using hooks with TypeScript. * Added documentation for creating a global Theme type. * Added tests for verifying that the examples in the docs behave well.
1 parent 8638d99 commit 39bae76

File tree

3 files changed

+203
-3
lines changed

3 files changed

+203
-3
lines changed

docs/react-jss-ts.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Using TypeScript with React-JSS
2+
3+
React-JSS enables you to supply types for your `ruleNames`, `props`/`data`, and `theme` both explicitly and implicitly.
4+
5+
## Explicit Typing
6+
7+
```tsx
8+
// Define Component
9+
type RuleNames = 'myButton' | 'myLabel'
10+
11+
interface ButtonProps {
12+
children?: React.ReactNode
13+
spacing?: number
14+
fontWeight?: string
15+
labelColor?: string
16+
}
17+
18+
interface CustomTheme {
19+
background: string
20+
}
21+
22+
const useStyles = createUseStyles<RuleNames, ButtonProps, CustomTheme>({
23+
myButton: {
24+
padding: ({spacing}) => spacing || 10
25+
},
26+
myLabel: ({theme, ...props}) => ({
27+
display: 'block',
28+
color: props.labelColor || 'red',
29+
fontWeight: props.fontWeight || 'bold',
30+
backgroundColor: theme.background || 'gray'
31+
})
32+
})
33+
34+
function Button({children, ...props}: ButtonProps): React.ReactElement {
35+
const theme = useTheme<CustomTheme>()
36+
const classes = useStyles({...props, theme})
37+
38+
return (
39+
<button className={classes.myButton}>
40+
<span className={classes.myLabel}>{children}</span>
41+
</button>
42+
)
43+
}
44+
45+
// Create App
46+
const theme = {background: 'black'}
47+
48+
function App(): React.ReactElement {
49+
return (
50+
<ThemeProvider theme={theme}>
51+
<Button fontWeight="bold">Submit</Button>
52+
</ThemeProvider>
53+
)
54+
}
55+
```
56+
57+
You only need to supply as much type information as you need. For instance, if you aren't using a `Theme`, then you don't need to supply a type for it. You'll notice that if you want to supply types for `Props` or `Theme`, you'll have to supply the type for `RuleNames` first. This means that if you want autocomplete for the `classes` that come from `useStyles`, you'll need to explicitly provide the strings you'll be using. This restriction is due to a limitation in TypeScript [that will hopefully get resolved soon](https://github.com/microsoft/TypeScript/pull/26349). However, you can see the next section for a workaround.
58+
59+
## Implicit Typing
60+
61+
If you want to provide `Props` and/or `Theme` types without having to supply the `RuleNames` type, you can make TypeScript infer the types implicitly. The primary benefit of doing this is that you'll get autocomplete on `classes` _in addition to_ your props/theme without having to duplicate your rule names. There are two approaches:
62+
63+
Using the functional variant...
64+
65+
```tsx
66+
const useStyles = createUseStyles((theme: CustomTheme) => ({
67+
myButton: {
68+
padding: (props: ButtonProps) => props.spacing || 10
69+
},
70+
myLabel: props => ({
71+
display: 'block',
72+
color: props.labelColor || 'red',
73+
fontWeight: props.fontWeight || 'bold',
74+
backgroundColor: theme.background || 'gray'
75+
})
76+
}))
77+
```
78+
79+
Or using the object variant...
80+
81+
```tsx
82+
const useStyles = createUseStyles({
83+
myLabel: ({theme, ...props}: ButtonProps & {theme: CustomTheme}) => ({
84+
display: 'block',
85+
color: props.labelColor || 'red',
86+
fontWeight: props.fontWeight || 'bold',
87+
backgroundColor: theme.background || 'gray'
88+
}),
89+
myButton: {
90+
padding: props => props.spacing || 10
91+
}
92+
})
93+
```
94+
95+
Note that because of how TS implicitly determines types, there are some restrictions here:
96+
97+
- For the functional variant, the `Theme` type _must_ be supplied with the `theme` argument to register its type. The `Props` type can be specified at any level in the object tree, but the first occurence of a data function _must_ provide the prop types.
98+
- For the object variant, both the `Theme` type _and_ the `Props` type _must_ be provided in the first occurence of a data function. Additionally, this data function must be defined at the rule name level of the object. Due to both of these restrictions, you'll notice that the `myLabel` definition was moved above `myButton` in the object variant example.
99+
100+
In both cases, the types only need to be supplied _once_ for TS to infer everything, as shown in the examples above. And again, you only need to define the types you choose to use.
101+
102+
## Defining a Global Default Theme
103+
104+
If `CustomTheme` is the same across your entire application, you can define a default theme for React-JSS to use by creating a file called `global.d.ts` anywhere in the project:
105+
106+
```typescript
107+
// global.d.ts
108+
109+
declare global {
110+
namespace Jss {
111+
export interface Thme {
112+
background: string
113+
}
114+
}
115+
}
116+
117+
export {}
118+
```
119+
120+
This enables React-JSS to rely on a default `Theme` type, removing the need to explicitly provide a type to `createUseStyles`. This file does not need to be imported anywhere; it is automatically acknowledged by TypeScript and/or VSCode's TypeScript Server.

docs/react-jss.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Try it out in the [playground](https://codesandbox.io/s/j3l06yyqpw).
3030
- [Custom setup](#custom-setup)
3131
- [Multi-tree setup](#multi-tree-setup)
3232
- [Injection order](#injection-order)
33-
- [TypeScript](#typescript)
33+
- [Usage with TypeScript](#usage-with-typescript)
3434

3535
## Install
3636

@@ -585,6 +585,6 @@ const Button = () => {
585585
}
586586
```
587587

588-
## TypeScript
588+
## Usage with TypeScript
589589

590-
TODO hooks support
590+
For help using TypeScript with React-JSS, go [here](react-jss-ts.md).
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React from 'react'
2+
import {createUseStyles, useTheme, ThemeProvider} from 'react-jss'
3+
4+
/* -------------------- EXPLICIT EXAMPLE -------------------- */
5+
// Define Component
6+
type RuleNames = 'myButton' | 'myLabel'
7+
8+
interface ButtonProps {
9+
children?: React.ReactNode
10+
spacing?: number
11+
fontWeight?: string
12+
labelColor?: string
13+
}
14+
15+
interface CustomTheme {
16+
background: string
17+
}
18+
19+
const useStyles = createUseStyles<RuleNames, ButtonProps, CustomTheme>({
20+
myButton: {
21+
padding: ({spacing}) => spacing || 10
22+
},
23+
myLabel: ({theme, ...props}) => ({
24+
display: 'block',
25+
color: props.labelColor || 'red',
26+
fontWeight: props.fontWeight || 'bold',
27+
backgroundColor: theme.background || 'gray'
28+
})
29+
})
30+
31+
function Button({children, ...props}: ButtonProps): React.ReactElement {
32+
const theme = useTheme<CustomTheme>()
33+
const classes = useStyles({...props, theme})
34+
35+
return (
36+
<button className={classes.myButton}>
37+
<span className={classes.myLabel}>{children}</span>
38+
</button>
39+
)
40+
}
41+
42+
// Create App
43+
const theme = {background: 'black'}
44+
45+
function App(): React.ReactElement {
46+
return (
47+
<ThemeProvider theme={theme}>
48+
<Button fontWeight="bold">Submit</Button>
49+
</ThemeProvider>
50+
)
51+
}
52+
53+
/* -------------------- IMPLICIT EXAMPLES -------------------- */
54+
// Note: `Theme` must be typed by the `theme` argument. And the
55+
// first occurence of a data function must provide `Props`.
56+
const useStylesImplicitFunc = createUseStyles((theme: CustomTheme) => ({
57+
myButton: {
58+
padding: (props: ButtonProps) => props.spacing || 10
59+
},
60+
myLabel: props => ({
61+
display: 'block',
62+
color: props.labelColor || 'red',
63+
fontWeight: props.fontWeight || 'bold',
64+
backgroundColor: theme.background || 'gray'
65+
})
66+
}))
67+
68+
// Note: First occurence of data function must provide `Props` and `Theme`.
69+
// It must also be defined at the `RuleNames` level.
70+
const useStylesImplicitObj = createUseStyles({
71+
myLabel: ({theme, ...props}: ButtonProps & {theme: CustomTheme}) => ({
72+
display: 'block',
73+
color: props.labelColor || 'red',
74+
fontWeight: props.fontWeight || 'bold',
75+
backgroundColor: theme.background || 'gray'
76+
}),
77+
myButton: {
78+
padding: props => props.spacing || 10
79+
}
80+
})

0 commit comments

Comments
 (0)