Skip to content

Commit f8082b9

Browse files
Add RecipeVariants type to get variants from recipes (#376)
1 parent ac09b5d commit f8082b9

File tree

5 files changed

+125
-0
lines changed

5 files changed

+125
-0
lines changed

.changeset/rare-lemons-study.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
'@vanilla-extract/recipes': minor
3+
---
4+
5+
Add `RecipeVariants` type
6+
7+
A utility to make use of the recipe’s type interface. This can be useful when typing functions or component props that need to accept recipe values as part of their interface.
8+
9+
```ts
10+
// button.css.ts
11+
import {
12+
recipe,
13+
RecipeVariants
14+
} from '@vanilla-extract/recipes';
15+
16+
export const button = recipe({
17+
variants: {
18+
color: {
19+
neutral: { background: 'whitesmoke' },
20+
brand: { background: 'blueviolet' },
21+
accent: { background: 'slateblue' }
22+
},
23+
size: {
24+
small: { padding: 12 },
25+
medium: { padding: 16 },
26+
large: { padding: 24 }
27+
}
28+
}
29+
});
30+
31+
// Get the type
32+
export type ButtonVariants = RecipeVariants<typeof button>;
33+
34+
// the above will result in a type equivalent to:
35+
export type ButtonVariants = {
36+
color?: 'neutral' | 'brand' | 'accent';
37+
size?: 'small' | 'medium' | 'large';
38+
};
39+
```

packages/recipes/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import type {
1010
VariantSelection,
1111
} from './types';
1212

13+
export type { RecipeVariants } from './types';
14+
1315
function mapValues<Input extends Record<string, any>, OutputValue>(
1416
input: Input,
1517
fn: (value: Input[keyof Input], key: keyof Input) => OutputValue,

packages/recipes/src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ export type PatternOptions<Variants extends VariantGroups> = {
3535
export type RuntimeFn<Variants extends VariantGroups> = (
3636
options?: VariantSelection<Variants>,
3737
) => string;
38+
39+
export type RecipeVariants<RecipeFn extends RuntimeFn<VariantGroups>> =
40+
Parameters<RecipeFn>[0];

site/docs/recipes-api.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,39 @@ export const button = recipe({
112112
}
113113
});
114114
```
115+
116+
## RecipeVariants
117+
118+
A utility to make use of the recipe’s type interface. This can be useful when typing functions or component props that need to accept recipe values as part of their interface.
119+
120+
```ts
121+
// button.css.ts
122+
import {
123+
recipe,
124+
RecipeVariants
125+
} from '@vanilla-extract/recipes';
126+
127+
export const button = recipe({
128+
variants: {
129+
color: {
130+
neutral: { background: 'whitesmoke' },
131+
brand: { background: 'blueviolet' },
132+
accent: { background: 'slateblue' }
133+
},
134+
size: {
135+
small: { padding: 12 },
136+
medium: { padding: 16 },
137+
large: { padding: 24 }
138+
}
139+
}
140+
});
141+
142+
// Get the type
143+
export type ButtonVariants = RecipeVariants<typeof button>;
144+
145+
// the above will result in a type equivalent to:
146+
export type ButtonVariants = {
147+
color?: 'neutral' | 'brand' | 'accent';
148+
size?: 'small' | 'medium' | 'large';
149+
};
150+
```
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
This file is for validating types, it is not designed to be executed
3+
*/
4+
import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
5+
6+
// @ts-expect-error Unused args
7+
const noop = (...args: Array<any>) => {};
8+
9+
type AssertIsString<S> = S extends string ? true : never;
10+
11+
() => {
12+
const textRecipes = recipe({
13+
variants: {
14+
size: {
15+
small: { fontSize: '12px' },
16+
medium: { fontSize: '16px' },
17+
large: { fontSize: '24px' },
18+
},
19+
},
20+
});
21+
22+
type TextVariants = RecipeVariants<typeof textRecipes>;
23+
24+
const invalidVariantValue: TextVariants = {
25+
// @ts-expect-error Type '"extraLarge"' is not assignable to type '"small" | "large" | "medium" | undefined'.
26+
size: 'extraLarge',
27+
};
28+
29+
const invalidVariantName: TextVariants = {
30+
// @ts-expect-error Property 'color' does not exist in type
31+
color: 'brand',
32+
};
33+
34+
const validTextVariant: TextVariants = {
35+
size: 'large',
36+
};
37+
38+
const recipeStyles = textRecipes({ size: 'small' });
39+
const recipeShouldReturnString: AssertIsString<typeof recipeStyles> = true;
40+
41+
noop(invalidVariantValue);
42+
noop(invalidVariantName);
43+
noop(validTextVariant);
44+
noop(recipeShouldReturnString);
45+
};

0 commit comments

Comments
 (0)