Skip to content

defineConsts arithmetic silently breaks inside stylex.create() #1597

@skovhus

Description

@skovhus

Describe the issue

defineConsts values are resolved to CSS var() references by the babel plugin when accessed inside stylex.create(). This means JavaScript arithmetic operators on defineConsts values silently produce wrong results — the + operator performs string concatenation on var() strings instead of numeric addition.

TypeScript does not catch this because defineConsts preserves the input types in its return type (StyleX$DefineConsts returns DefaultTokens), so numeric constants are typed as number even though they're strings at compile time.

Expected behavior

defineConsts values should behave as their literal values inside stylex.create(). Arithmetic like Constants.A + Constants.B where both are numeric constants should produce the correct numeric result (e.g., 26 + 14 = 40), not string concatenation ("var(--A-hash)var(--B-hash)").

Outside stylex.create(), the babel plugin correctly inlines defineConsts values as numeric literals, so arithmetic works there. The behavior should be consistent.

Steps to reproduce

@stylexjs/babel-plugin v0.18.2, @stylexjs/stylex v0.18.2

  1. Define numeric constants in a .stylex.ts file:
// constants.stylex.ts
import * as stylex from "@stylexjs/stylex";
export const C = stylex.defineConsts({
  A: 26,
  B: 14,
  D: 6,
});
  1. Use arithmetic on those constants inside stylex.create() in another file:
// component.tsx
import * as stylex from "@stylexjs/stylex";
import { C } from "./constants.stylex";

const styles = stylex.create({
  box: { height: C.A + C.B - C.D }, // Expected: 34px, Actual: NaN or wrong value
});
  1. The build succeeds with no errors, but the generated CSS has an incorrect height value.

Test case

The root cause is in evaluateThemeRef in the babel plugin. Both defineVars and defineConsts imports from .stylex files are routed through the same 'themeNameRef' codepath in importPathResolver, which returns a Proxy that resolves every property access to a var(--name) string:

// babel-plugin/lib/index.js — evaluateThemeRef (around line 5846)
function evaluateThemeRef(fileName, exportName, state) {
  const resolveKey = key => {
    // ...hashing...
    return `var(--${varName})`; // Always returns a string
  };
  const proxy = new Proxy({}, {
    get(_, key) {
      return resolveKey(key); // C.A becomes "var(--A-hash)"
    },
  });
  return proxy;
}

Then in the BinaryExpression evaluator (around line 6247):

case '+':
  return left + right; // "var(--A-hash)" + "var(--B-hash)" = string concatenation

Template literal interpolation (`${C.A}px`) works because String("var(--A-hash)") is valid and the post-processor resolves it. But arithmetic on two var() strings is nonsense.

Additional comments

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions