styled-components
Version:
Fast, expressive styling for React.
397 lines (396 loc) • 20.4 kB
TypeScript
import type * as CSS from 'csstype';
import React from 'react';
import ComponentStyle from './models/ComponentStyle';
import { DefaultTheme } from './models/ThemeProvider';
import createWarnTooManyClasses from './utils/createWarnTooManyClasses';
import type { SupportedHTMLElements } from './utils/domElements';
export { CSS, DefaultTheme, SupportedHTMLElements };
export interface ExoticComponentWithDisplayName<P extends BaseObject = {}> extends React.ExoticComponent<P> {
defaultProps?: Partial<P> | undefined;
displayName?: string | undefined;
}
/**
* Use this type to disambiguate between a styled-component instance
* and a StyleFunction or any other type of function.
*/
export type StyledComponentBrand = {
readonly _sc: symbol;
};
export type BaseObject = {};
export type OmitNever<T> = {
[K in keyof T as T[K] extends never ? never : K]: T[K];
};
export type FastOmit<T extends BaseObject, U extends string | number | symbol> = {
[K in keyof T as K extends U ? never : K]: T[K];
};
export type Runtime = 'web' | 'native';
export type AnyComponent<P extends BaseObject = any> = ExoticComponentWithDisplayName<P> | React.ComponentType<P>;
export type KnownTarget = SupportedHTMLElements | AnyComponent;
export type WebTarget = (string & {}) | KnownTarget;
export type NativeTarget = AnyComponent;
export type StyledTarget<R extends Runtime> = R extends 'web' ? WebTarget : NativeTarget;
export interface StyledOptions<R extends Runtime, Props extends BaseObject> {
attrs?: Attrs<Props>[] | undefined;
componentId?: (R extends 'web' ? string : never) | undefined;
displayName?: string | undefined;
parentComponentId?: (R extends 'web' ? string : never) | undefined;
shouldForwardProp?: ShouldForwardProp<R> | undefined;
}
export type Dict<T = any> = {
[key: string]: T;
};
/**
* This type is intended for when data attributes are composed via
* the `.attrs` API:
*
* ```tsx
* styled.div.attrs<DataAttributes>({ 'data-testid': 'foo' })``
* ```
*
* Would love to figure out how to support this natively without having to
* manually compose the type, but haven't figured out a way to do so yet that
* doesn't cause specificity loss (see `test/types.tsx` if you attempt to embed
* `DataAttributes` directly in the `Attrs<>` type.)
*/
export type DataAttributes = {
[key: `data-${string}`]: any;
};
export type ExecutionProps = {
/**
* Dynamically adjust the rendered component or HTML tag, e.g.
* ```
* const StyledButton = styled.button``
*
* <StyledButton as="a" href="/foo">
* I'm an anchor now
* </StyledButton>
* ```
*/
as?: KnownTarget | undefined;
forwardedAs?: KnownTarget | undefined;
theme?: DefaultTheme | undefined;
};
/**
* ExecutionProps but with `theme` narrowed from optional to required.
*
* Note: in RSC environments where ThemeProvider is a no-op,
* `theme` will be `undefined` at runtime.
*/
export interface ExecutionContext extends ExecutionProps {
theme: DefaultTheme;
}
export interface StyleFunction<Props extends BaseObject> {
(executionContext: ExecutionContext & Props): Interpolation<Props>;
}
export type Interpolation<Props extends BaseObject> = StyleFunction<Props> | StyledObject<Props> | TemplateStringsArray | string | number | false | undefined | null | Keyframes | StyledComponentBrand | RuleSet<Props>;
export type Attrs<Props extends BaseObject = BaseObject> = (ExecutionProps & Partial<Props>) | ((props: ExecutionContext & Props) => ExecutionProps & Partial<Props>);
export type RuleSet<Props extends BaseObject = BaseObject> = Interpolation<Props>[];
export type Styles<Props extends BaseObject> = TemplateStringsArray | StyledObject<Props> | StyleFunction<Props>;
export type NameGenerator = (hash: number) => string;
export interface StyleSheet {
create: Function;
}
export interface Keyframes {
id: string;
name: string;
rules: string;
}
export interface Flattener<Props extends BaseObject> {
(chunks: Interpolation<Props>[], executionContext: object | null | undefined, styleSheet: StyleSheet | null | undefined): Interpolation<Props>[];
}
export interface Stringifier {
(css: string, selector?: string | undefined, prefix?: string | undefined, componentId?: string | undefined): string[];
hash: string;
}
export interface ShouldForwardProp<R extends Runtime> {
(prop: string, elementToBeCreated: StyledTarget<R>): boolean;
}
export interface CommonStatics<out R extends Runtime, in out Props extends BaseObject> {
attrs: Attrs<Props>[];
target: StyledTarget<R>;
shouldForwardProp?: ShouldForwardProp<R> | undefined;
}
export interface IStyledStatics<out R extends Runtime, in out OuterProps extends BaseObject> extends CommonStatics<R, OuterProps> {
componentStyle: R extends 'web' ? ComponentStyle : never;
foldedComponentIds: R extends 'web' ? string : never;
inlineStyle: R extends 'native' ? InstanceType<IInlineStyleConstructor<OuterProps>> : never;
target: StyledTarget<R>;
styledComponentId: R extends 'web' ? string : never;
warnTooManyClasses?: (R extends 'web' ? ReturnType<typeof createWarnTooManyClasses> : never) | undefined;
}
/** ExecutionProps sans as/forwardedAs, pre-resolved so call sites relate against a concrete interface. */
interface ThemedExecutionProps {
theme?: DefaultTheme | undefined;
}
/**
* Props of a render target, for `as` / `forwardedAs`.
*
* One distributive conditional, never two nested, and tags resolve by indexed
* access rather than `React.ComponentPropsWithRef`. Both are load-bearing: this
* shape is the #5767 fix, and nesting a `T extends KnownTarget` check around it
* costs ~4x the check time. The `AnyComponent` arm doubles as that test, and
* every non-target falls through to `{}`.
*
* The `style` widening happens here, once per target, rather than at every JSX
* call site -- directly via {@link WithCSSVars} on the intrinsic arm, which
* needs no guard, and via {@link OverrideStyle} on the component arm, which
* does. See AGENTS.md before changing any of it.
*
* `R` carries the runtime so the widening stays web-only; it is deliberately
* undefaulted, since a default is what would let a native call site pick up web
* CSS by omission.
*/
export type TargetProps<R extends Runtime, T> = T extends keyof React.JSX.IntrinsicElements ? IntrinsicProps<T> : T extends AnyComponent ? ComponentTargetProps<R, T> : {};
/**
* Props of an HTML or SVG tag.
*
* Both branches of {@link TargetProps} are named rather than inlined, so a
* component's type reads as `Substituted<IntrinsicProps<"button">, { … }>`
* instead of the full expansion of every tag attribute. See {@link WithCSSVars}
* for why a conditional's inline branch cannot keep a name.
*
* Applies the widening directly rather than through {@link OverrideStyle}: every
* intrinsic element declares `style`, so the guard has nothing to decide here.
*/
type IntrinsicProps<T extends keyof React.JSX.IntrinsicElements> = WithCSSVars<React.JSX.IntrinsicElements[T]>;
/**
* Props of a component render target. Named for the same reason as {@link IntrinsicProps}.
*
* The `style` widening is web-only: a React Native `style` takes a `ViewStyle`,
* which carries neither web CSS nor custom properties. This is the only seam that
* knows the runtime, which is why the gate sits here rather than inside
* {@link OverrideStyle}. The conditional is over `Runtime` -- two members, concrete
* at every entry point -- never over the target union.
*/
type ComponentTargetProps<R extends Runtime, T extends AnyComponent> = R extends 'web' ? OverrideStyle<React.ComponentPropsWithRef<T>> : React.ComponentPropsWithRef<T>;
/**
* Used by PolymorphicComponent to define prop override cascading order.
*/
export type PolymorphicComponentProps<R extends Runtime, BaseProps extends BaseObject, AsTarget extends StyledTarget<R> | (BaseProps extends {
as?: infer A;
} ? A : never) | void, ForwardedAsTarget extends StyledTarget<R> | void, AsTargetProps extends BaseObject = TargetProps<R, AsTarget>, ForwardedAsTargetProps extends BaseObject = TargetProps<R, ForwardedAsTarget>> = NoInfer<FastOmit<MergeProps<BaseProps, Substitute<ForwardedAsTargetProps, AsTargetProps>>, keyof ExecutionProps>> & ThemedExecutionProps & {
as?: AsTarget;
forwardedAs?: ForwardedAsTarget;
};
/**
* Resolves the call-site props for one usage of a polymorphic component from its
* `as` / `forwardedAs` targets. An `as` render target has its props merged over
* the base props and requires `as`; plain usage (or `as` being the wrapped
* component's own non-target type, e.g. Next.js Link's `as?: Url`) reaches
* {@link PolymorphicComponentProps} not at all, so the base props stay untouched
* and ref callbacks infer with spread props (#5687), the wrapped `as` stays
* assignable (#5734), and BaseProps keys keep completing (#5741). `forwardedAs`
* merges the same way, and loses to `as` where both name a target.
*
* The target test is `string | AnyComponent`, not `KnownTarget`: narrowing it
* drops custom element strings (`as="my-element"`) out of the target branch.
*
* Load-bearing shape, do not simplify: two conditionals with `unknown` sibling
* branches (not one three-way conditional), a leading flat `{ as?; forwardedAs? }`
* member, and positive `extends [string | AnyComponent]` discriminants. Collapsing
* the conditionals, dropping the flat member, or using a `[void]` discriminant
* each regress plain-call-site cost, `as`-target completion, or ref-callback
* inference (#5687) respectively.
*/
type PolymorphicCallProps<R extends Runtime, BaseProps extends BaseObject, AsTarget extends StyledTarget<R> | (BaseProps extends {
as?: infer A;
} ? A : never) | void, ForwardedAsTarget extends StyledTarget<R> | void> = {
as?: AsTarget | undefined;
forwardedAs?: ForwardedAsTarget | undefined;
} & ([
AsTarget
] extends [string | AnyComponent] ? PolymorphicComponentProps<R, BaseProps, AsTarget, ForwardedAsTarget> & {
as: AsTarget;
} : unknown) & ([AsTarget] extends [string | AnyComponent] ? unknown : [ForwardedAsTarget] extends [string | AnyComponent] ? PolymorphicComponentProps<R, BaseProps, void, ForwardedAsTarget> & {
forwardedAs: ForwardedAsTarget;
} : NoInfer<FastOmit<BaseProps, keyof ExecutionProps>> & ThemedExecutionProps);
/**
* This type forms the signature for a forwardRef-enabled component
* that accepts the "as" prop to dynamically change the underlying
* rendered JSX. The interface will automatically attempt to extract
* props from the given rendering target to get proper typing for
* any specialized props in the target component.
*/
export interface PolymorphicComponent<out R extends Runtime, in out BaseProps extends BaseObject> extends React.ForwardRefExoticComponent<FastOmit<BaseProps, 'as' | 'forwardedAs'> & {
as?: StyledTarget<R> | undefined;
forwardedAs?: StyledTarget<R> | undefined;
}> {
<AsTarget extends StyledTarget<R> | (BaseProps extends {
as?: infer A;
} ? A : never) | void = void, ForwardedAsTarget extends StyledTarget<R> | void = void>(props: PolymorphicCallProps<R, BaseProps, AsTarget, ForwardedAsTarget>): React.JSX.Element;
}
/**
* Some wrapped targets can't be statically introspected and their props
* collapse to `{}` -- most notably polymorphic-factory components (e.g. Mantine
* v7's `Button`, `Card`, `Menu.Item`), whose generic callable signature defeats
* `React.ComponentPropsWithRef`. A closed `{}` would reject every prop at the JSX
* call site, including `children`. Falling back to a permissive prop bag keeps
* these components usable; targets with introspectable props are unchanged.
*
* Applied only to the JSX call surface (`PolymorphicComponent`), never to the
* statics (`IStyledStatics`, `defaultProps`), so internal code keeps the real
* `Props` and the widening can't leak past the call site.
*
* The test distributes over `Props` first: `keyof` on a union intersects each
* member's keys, so a union of disjoint shapes has `keyof` of `never` while
* being perfectly introspectable. Checking each member alone avoids widening it.
*/
export type WidenUntypedProps<Props extends BaseObject> = WidenForUntypedTarget<Props, Props>;
/**
* Widens because the *target* is un-introspectable, even when the component
* declares props of its own.
*
* `Target` must be the target's props, never a bag the component's own props
* were merged into. Pass the latter and the test degrades: adding one transient
* prop makes `keyof` non-`never`, the widening switches off, and the target's
* own props including `children` start being rejected. That is #5756, and every
* call site here passes `TargetProps<R, Target>` for that reason.
*
* Applying it to an already-widened `Target` is a no-op, since the index
* signature makes `keyof` be `string`.
*/
export type WidenForUntypedTarget<Target extends BaseObject, Props extends BaseObject> = (Target extends unknown ? (keyof Target extends never ? true : false) : never) extends true ? Props & {
[key: string]: unknown;
} : Props;
export interface IStyledComponentBase<out R extends Runtime, in out Props extends BaseObject = BaseObject> extends PolymorphicComponent<R, WidenUntypedProps<Props>>, IStyledStatics<R, Props>, StyledComponentBrand {
defaultProps?: (ExecutionProps & Partial<Props>) | undefined;
toString: () => string;
}
/**
* Intersected with `string` so styled components can be used as computed
* property keys in object styles: `{ [MyComponent]: { ... } }`.
* The conditional `R extends 'web' ? string : {}` was removed to avoid
* a type alias with a conditional - type aliases require full structural
* comparison on every use, while this unconditional intersection is cheaper.
*/
export type IStyledComponent<R extends Runtime, Props extends BaseObject = BaseObject> = IStyledComponentBase<R, Props> & string;
export interface IStyledComponentFactory<out R extends Runtime, in Target extends StyledTarget<R>, in out OuterProps extends BaseObject, out OuterStatics extends BaseObject = BaseObject> {
<Props extends BaseObject = BaseObject, Statics extends BaseObject = BaseObject>(target: Target, options: StyledOptions<R, OuterProps & Props>, rules: RuleSet<OuterProps & Props>): IStyledComponent<R, Substitute<OuterProps, Props>> & OuterStatics & Statics;
}
export interface IInlineStyleConstructor<Props extends BaseObject> {
new (rules: RuleSet<Props>): IInlineStyle<Props>;
}
export interface IInlineStyle<Props extends BaseObject> {
rules: RuleSet<Props>;
generateStyleObject(executionContext: ExecutionContext & Props): object;
}
export type CSSProperties = CSS.Properties<number | (string & {})>;
export type CSSPropertiesWithVars = CSSProperties & {
[key: `--${string}`]: string | number | undefined;
};
/**
* A `style` type that accepts exactly the fields given and nothing else.
*
* A declared `style` normally narrows the fields it names and leaves the rest of
* CSS available, which is what you want when constraining one or two properties:
*
* ```tsx
* // `width` must be a number; `color` and custom properties still work
* const Box = styled.div<{ style?: { width: number } }>``;
* ```
*
* Wrap the declaration in `CustomStyle` when the point is to forbid everything
* else, rather than writing `color?: never` for every property by hand:
*
* ```tsx
* // `width` is the only accepted style field
* const Box = styled.div<{ style?: CustomStyle<{ width: number }> }>``;
* ```
*/
export type CustomStyle<T extends object> = T & {
[K in Exclude<keyof CSSPropertiesWithVars, keyof T>]?: never;
};
/**
* Widens a target's `style` prop so CSS custom properties are accepted, and the
* taken branch of {@link OverrideStyle}. Keep it named: a conditional alias
* loses its name once it resolves, so an inline branch prints its whole
* expansion in every hover and error.
*
* `(P['style'] & {})` is load-bearing under `exactOptionalPropertyTypes` -- it
* filters `undefined` out so the `?:` stays the sole optional source -- and the
* explicit `| undefined` then restores `style={undefined}`.
*/
type WithCSSVars<P extends BaseObject> = Omit<P, 'style'> & {
style?: CSSPropertiesWithVars | (P[keyof P & 'style'] & {}) | undefined;
};
/**
* Applies the `style` widening to a target that may or may not declare `style`.
*
* Applied once per target in {@link TargetProps}, never to a merged prop bag at
* a JSX call site. It runs before a component's own props, which then merge over
* it via {@link MergeProps} rather than replacing it.
*
* The test is `'style' extends keyof P`, not `P extends { style?: infer S }`:
* the latter is vacuously satisfied by `{}`, which would hand a `style` key to
* targets that expose no props at all and defeat `WidenUntypedProps` (#5756).
* Only {@link ComponentTargetProps} needs the guard; every intrinsic element
* declares `style`, so {@link IntrinsicProps} applies `WithCSSVars` directly.
*/
type OverrideStyle<P extends BaseObject> = 'style' extends keyof P ? WithCSSVars<P> : P;
export type CSSPseudos = {
[K in CSS.Pseudos]?: CSSObject;
};
export type CSSKeyframes = object & {
[key: string]: CSSObject;
};
export type CSSObject<Props extends BaseObject = BaseObject> = StyledObject<Props>;
export interface StyledObject<Props extends BaseObject = BaseObject> extends CSSProperties, CSSPseudos {
[key: string]: StyledObject<Props> | string | number | StyleFunction<Props> | RuleSet<any> | undefined;
}
/**
* The `css` prop is not declared by default in the types as it would cause `css` to be present
* on the types of anything that uses styled-components indirectly, even if they do not use the
* babel plugin.
*
* To enable support for the `css` prop in TypeScript, create a `styled-components.d.ts` file in
* your project source with the following contents:
*
* ```ts
* import type { CSSProp } from "styled-components";
*
* declare module "react" {
* interface Attributes {
* css?: CSSProp;
* }
* }
* ```
*
* In order to get accurate typings for `props.theme` in `css` interpolations, see
* {@link DefaultTheme}.
*/
export type CSSProp = Interpolation<any>;
export type { NoInfer } from './utils/noInfer';
/** The taken branch of {@link Substitute}. Named so it survives into hovers and
* error messages; see {@link WithCSSVars} for why an inline branch does not. */
export type Substituted<A extends BaseObject, B> = FastOmit<A, keyof B> & B;
export type Substitute<A extends BaseObject, B> = keyof B extends never ? A : Substituted<A, B>;
/**
* A component's own props over its target's props, with `style` merged rather
* than replaced, so `styled.div<{ style?: { width: number } }>` constrains
* `width` and leaves the rest of CSS accepted. A field declared `never` is
* removed; {@link CustomStyle} removes everything a declaration omits.
*
* Under `exactOptionalPropertyTypes` the intersection leaves no `undefined` arm,
* so such a component rejects an explicit `style={undefined}`; declare
* `style?: X | undefined` to allow it. Omitting the prop is unaffected.
*
* Both conditional spellings of this were measured and rejected, one of them
* fatal. Keep it an intersection; see AGENTS.md before changing the shape.
*/
export type MergeProps<A extends BaseObject, B> = keyof B extends never ? A : Merged<A, B>;
/** The taken branch of {@link MergeProps}, named so hovers print a name rather
* than the expansion. Keep it named; see {@link Substituted}. */
export type Merged<A extends BaseObject, B> = FastOmit<A, Exclude<keyof B, 'style'>> & B;
/**
* Makes keys in K optional while keeping all others required.
* Used to make attrs-provided props optional on the final component.
*
* The guard is `[K] extends [never]`, not `keyof K extends never`. `K` is the set
* of attrs-provided keys and is `never` for any component without `.attrs()`,
* which is most of them, but `keyof never` is `string | number | symbol`, so the
* old spelling never short-circuited. Every such component paid an omit plus a
* `Partial<Pick<...>>` that removed and re-added nothing, and carried both in its
* displayed type.
*/
export type MakeAttrsOptional<P extends BaseObject, K extends keyof any> = [K] extends [never] ? P : FastOmit<P, K & keyof P> & Partial<Pick<P, K & keyof P>>;
export type InsertionTarget = HTMLElement | ShadowRoot;