Skip to content

feat: Boolean Shorthand for Complex/ Union Types #6216

Open
@kaiszybiak

Description

@kaiszybiak

Prerequisites

Describe the Feature Request

Stencil should support boolean shorthand (<my-component compact></my-component>) for complex or union types that include boolean. Currently, when a prop is defined as a union type (e.g., boolean | number | string), Stencil defaults it to any, breaking shorthand behavior.

propTypeFromTSType:

  // if type is more than a primitive type at the same time, we mark it as any
  if (Number(isStr) + Number(isNu) + Number(isBool) > 1) {
    return 'any';
  }

Describe the Use Case

We use a generic type called BreakpointCustomizable<T>, allowing properties to be customized per breakpoint. For example:

export const breakpoints = ['base', 'xs', 's', 'm', 'l', 'xl', 'xxl'] as const;
export type Breakpoint = (typeof breakpoints)[number];
export type BreakpointValues<T> = {
  [key in Breakpoint]?: T;
} & {
  base: T;
};

export type BreakpointCustomizable<T> = T | BreakpointValues<T> | string;

@Prop() public compact?: BreakpointCustomizable<boolean> = false;

This enables usage like:

<my-component compact="{ base: true, m: false }"></my-component>

However, shorthand usage fails:

<my-component compact></my-component>

Since Stencil marks mixed primitive types as any, it does not apply boolean parsing logic, preventing shorthand props from resolving correctly.

Describe Preferred Solution

A new shorthand option could be introduced in the @Prop decorator configuration to explicitly enable shorthand attribute behavior.

  • If shorthand: true is set, an empty attribute (<my-component compact>) will always be interpreted as true, regardless of the prop’s type—including complex or union types.

  • This provides a way to preserve shorthand behavior for props that would otherwise default to any in Stencil’s type system, where shorthand behavior is lost.

  • Making this an explicit opt-in avoids altering Stencil’s existing type inference while giving developers control over shorthand handling.

Potential API Change
One way to achieve this would be to extend the PropOptions interface:

export interface PropOptions {
  attribute?: string | null;
  mutable?: boolean;
  reflect?: boolean;
  /**
   * Ensures an empty attribute (`<my-component prop>`) is always interpreted as `true`,
   * even for complex or union types.
   */
  shorthand?: boolean;
}
export class MyComponent {
  @Prop({ shorthand: true }) compact?: BreakpointCustomizable<boolean>;
}

Describe Alternatives

Add an option to the @Prop decorator that allows developers to provide a custom parser function directly. This option, for instance named parseValue, would enable custom parsing logic for individual props without affecting global behavior. For example:

@Prop({
  parseValue: (propValue, propType) => {
    // Custom logic for handling boolean shorthand:
    if (propValue === '') {
      return true;
    }
    // Fallback to the default.
    return propValue;
  }
})
compact?: BreakpointCustomizable<boolean>;

This solution provides granular control, ensuring that only the props that need special treatment are affected, while the default behavior remains unchanged elsewhere. It also enhances the flexibility and expressiveness of the decorator, allowing developers to tailor parsing behavior to fit complex type scenarios like BreakpointCustomizable<boolean>.

Related Code

No response

Additional Information

Our team at Porsche Design System is currently working on a workaround for this behavior internally. We are actively tracking our progress here: Porsche Design System PR #3795.

Currently, our patch modifies parsePropertyValue with the following logic:

if (propType & MEMBER_FLAGS.Any) {
  return propValue === '' ? true : propValue;
}

While this approach maybe works for our use case, it is not ideal. The main issue arises when a prop has a union type such as string | number. In this case:

<my-component some-prop></my-component>

The empty string ("") would incorrectly be interpreted as true, which is unintended behavior. This is one of the reasons we believe a built-in solution in Stencil would be preferable.

We are interested in discussing possible improvements and whether Stencil should support a more robust solution natively.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions