Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/css/src/compat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ export {
css as style,
cssMultiple as styleVariants
} from "./css/index.js";
export type { CSSRuleWith as StyleRuleWith } from "./css/types.js";

export type {
VariantStyle,
RulesVariants,
RecipeVariants,
RuntimeFn,
VariantGroups,
PatternOptions,
Expand Down
6 changes: 3 additions & 3 deletions packages/css/src/css/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { setFileScope } from "@vanilla-extract/css/fileScope";
import { style as vStyle, globalStyle as gStyle } from "@vanilla-extract/css";
import type { GlobalStyleRule } from "@vanilla-extract/css";
import { className, getDebugName } from "../utils.js";
import type { RestrictCSSRule } from "./types.js";
import type { CSSRuleWith } from "./types.js";

// == Global CSS ===============================================================
export function globalCss(selector: string, rule: GlobalCSSRule) {
Expand Down Expand Up @@ -111,9 +111,9 @@ function cssRaw(style: ComplexCSSRule) {
}

function cssWith<const T extends CSSRule>(
callback?: (style: RestrictCSSRule<T>) => ComplexCSSRule
callback?: (style: CSSRuleWith<T>) => ComplexCSSRule
) {
type RestrictedCSSRule = RestrictCSSRule<T>;
type RestrictedCSSRule = CSSRuleWith<T>;
const cssFunction = callback ?? ((style: RestrictedCSSRule) => style);

function cssWithImpl(style: RestrictedCSSRule, debugId?: string) {
Expand Down
91 changes: 89 additions & 2 deletions packages/css/src/css/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ type IsOptional<T, K extends keyof T> =
type IsRequired<T, K extends keyof T> =
IsOptional<T, K> extends true ? false : true;

export type RestrictCSSRule<T extends CSSRule> = {
[K in keyof T as T[K] extends false ? never : K]: T[K] extends true
export type CSSRuleWith<T extends CSSRule> = {
[K in keyof T as T[K] extends false ? never : K]: true extends T[K]
? K extends keyof CSSRule
? NonNullable<CSSRule[K]>
: never
Expand All @@ -21,3 +21,90 @@ export type RestrictCSSRule<T extends CSSRule> = {
}
>
: never;

// == Tests ====================================================================
// Ignore errors when compiling to CommonJS.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore error TS1343: The 'import.meta' meta-property is only allowed when the '--module' option is 'es2020', 'es2022', 'esnext', 'system', 'node16', or 'nodenext'.
if (import.meta.vitest) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore error TS1343: The 'import.meta' meta-property is only allowed when the '--module' option is 'es2020', 'es2022', 'esnext', 'system', 'node16', or 'nodenext'.
const { describe, it, assertType } = import.meta.vitest;

describe.concurrent("CSSRuleWith Type Test", () => {
function assertCssRuleWith<const T extends CSSRule>(
variants: CSSRuleWith<T>
) {
assertType<CSSRuleWith<T>>(variants);
return variants;
}

it("Common CSSRuleWith Type", () => {
assertCssRuleWith({
color: "red",
// @ts-expect-error: `size` is not css property.
size: "small"
});
});

it("Based on CSSRule Type", () => {
type TestRule = {
color: true;
background?: true;
};

assertCssRuleWith<TestRule>({
color: "red"
// background is optional
});
assertCssRuleWith<TestRule>({
color: "red",
background: "blue"
});

// @ts-expect-error: `color` is required.
assertCssRuleWith<TestRule>({
background: "blue"
});
assertCssRuleWith<TestRule>({
color: "red",
// @ts-expect-error: backgroundColor is not included in the type.
backgroundColor: "blue"
});
});

it("Custom RuleWith Type", () => {
type TestRule = {
color: true;
size: "medium" | "large";
variant?: "outlined" | "filled";
};

assertCssRuleWith<TestRule>({
color: "red",
size: "medium"
});
assertCssRuleWith<TestRule>({
color: "red",
size: "medium",
variant: "outlined"
});

// @ts-expect-error: `size` is required.
assertCssRuleWith<TestRule>({
color: "red"
});
assertCssRuleWith<TestRule>({
color: "red",
// @ts-expect-error: small is not included in `size` key.
size: "small"
});
assertCssRuleWith<TestRule>({
color: "red",
size: "medium",
// @ts-expect-error: `variant` only accepts "outlined" | "filled".
variant: "ghost"
});
});
});
}
4 changes: 2 additions & 2 deletions packages/css/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ export {
} from "@vanilla-extract/css";

export { globalCss, css } from "./css/index.js";
export type { CSSRuleWith } from "./css/types.js";
export { rules } from "./rules/index.js";
export type {
VariantStyle,
RulesVariants,
RecipeVariants,
RulesVariants as RecipeVariants,
RuntimeFn,
VariantGroups,
PatternOptions,
Expand Down
11 changes: 4 additions & 7 deletions packages/css/src/rules/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
NonNullableString
} from "@mincho-js/transform-to-vanilla";
import type { Resolve } from "../types.js";
import type { CSSRuleWith } from "../css/types.js";

export type ResolveComplex<T> =
T extends Array<infer U> ? Array<Resolve<U>> : Resolve<T>;
Expand Down Expand Up @@ -33,7 +34,9 @@ export type VariantStyle<
VariantNames extends string,
CssRule extends RecipeStyleRule = RecipeStyleRule
> = {
[VariantName in VariantNames]: CssRule;
[VariantName in VariantNames]: CssRule extends CSSRule
? CSSRuleWith<CssRule>
: CssRule;
};

// Same of VariantMap but for fast type checking
Expand Down Expand Up @@ -210,12 +213,6 @@ export type RulesVariants<
ComplexPropDefinitions<PropTarget | undefined>
>
> = ResolveComplex<Parameters<RuleFn>[0]>;
export type RecipeVariants<
RecipeFn extends RuntimeFn<
VariantGroups,
ComplexPropDefinitions<PropTarget | undefined>
>
> = RulesVariants<RecipeFn>;

// == Tests ====================================================================
// Ignore errors when compiling to CommonJS.
Expand Down