|
| 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. |
0 commit comments