Description
Prerequisites
- I have read the Contributing Guidelines.
- I agree to follow the Code of Conduct.
- I have searched for existing issues that already include this feature request, without success.
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 astrue
, 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.