Unreleased changes
These pages document main, which has changes 2.1.1 does not. npm install @evanion/compose gives you 2.1.1, documented here. Values on these pages are produced by running the current source on every build.
Keeping the check
@evanion/compose provides powerful compile-time prop validation, but that validation depends on how you declare your provider arrays. If you move your providers into a named constant or a wrapper component, you might accidentally “silence” the type checker.
The Danger of Type Widening
In TypeScript, when you declare an array, the compiler often “widens” the type. Instead of seeing a fixed-length tuple of specific components and props, it sees a general array of (Component | Props)[].
When a type is widened, ComposeProvider can no longer correlate a specific component with its specific props. As a result, the prop validation fails silently.
Maintaining Checks in Named Arrays
If you define your provider list as a constant, you must use as const. This tells TypeScript to treat the array as a read-only tuple with a fixed length and exact values, preserving the a-priori type information needed for validation.
import type { } from '@evanion/compose';
const : = [[, {}]];
const = [[, {}]] as ;
const = [[, { : 'dark' }]];
// Compiled for their errors only: `constant` misses `theme`, and `plain` has
// widened to an array that is not a provider.
< providers={}>{null}</>;< providers={}>{null}</>;
const = (
< ={}>
<>Brass: Birmingham</>
</>,
);
; // -> '<div><p>Brass: Birmingham</p></div>'Comparing Declaration Styles:
| Declaration | Validation Status | Result |
|---|---|---|
providers={[...]}, inline | âś… Fully Checked | Every entry is validated. |
const providers = [...] as const | âś… Fully Checked | Every entry is validated. |
const providers = [...] | ❌ Widened | Prop checks are lost; hand-written tuples are rejected. |
const providers: ProviderArray = [...] | ❌ Silenced | Type is too general; reports no errors. |
const providers = [...] satisfies ProviderArray | ❌ Silenced | Type is too general; reports no errors. |
Pro Tip: Using the provider() helper avoids this entire problem. Because provider() checks props at the call site, the check is preserved regardless of how the resulting tuple is stored.
Preserving Checks in Wrapper Components
It is common to create a custom wrapper component (e.g., <AppProviders />) to encapsulate your root provider logic. However, if you type the providers prop simply as ProviderArray, you’ve silenced the check for anyone using your wrapper.
To fix this, use a const type parameter. This forces TypeScript to infer the exact, fixed-length tuple of the caller’s array.
import type { , } from '@evanion/compose';
function <const extends >({
,
,
}: <>) {
return < ={}>{}</>;
}
// Compiled for its error only: the caller's tuple misses `theme`.
< ={[, [, {}]]}> {null}
</>;
const = (
< ={[]}>
<>Brass: Birmingham</>
</>,
);
; // -> '<div id="cart"><p>Brass: Birmingham</p></div>'How it Works:
<const T extends ProviderArray>: Theconstmodifier tells TypeScript to inferTas the most specific literal type possible (a fixed-length tuple).ComposeProviderProps<T>: By typing the props asComposeProviderProps<T>, the validation logic ofComposeProvideris passed through to the caller of your wrapper.
Now, if a developer uses your <AppProviders /> and passes a ThemeProvider with missing props, they will get a compile error at the call site.
Every @evanion/compose export
The API reference lists every export of @evanion/compose, including ValidateProvider, the type that produces the error messages described in Where the check happens.