@types/styled-components
Version:
TypeScript definitions for styled-components
431 lines (370 loc) • 17.4 kB
TypeScript
// forward declarations
declare global {
namespace NodeJS {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ReadableStream {}
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ShadowRoot {}
}
import * as CSS from "csstype";
import hoistNonReactStatics = require("hoist-non-react-statics");
import * as React from "react";
export type CSSProperties = CSS.Properties<string | number>;
export type CSSPseudos = { [K in CSS.Pseudos]?: CSSObject };
export interface CSSObject extends CSSProperties, CSSPseudos {
[key: string]: CSSObject | string | number | undefined;
}
export type CSSKeyframes = object & { [key: string]: CSSObject };
export interface ThemeProps<T> {
theme: T;
}
export type ThemedStyledProps<P, T> = P & ThemeProps<T>;
export type StyledProps<P> = ThemedStyledProps<P, AnyIfEmpty<DefaultTheme>>;
export type IntrinsicElementsKeys = keyof React.JSX.IntrinsicElements;
// Any prop that has a default prop becomes optional, but its type is unchanged
// Undeclared default props are augmented into the resulting allowable attributes
// If declared props have indexed properties, ignore default props entirely as keyof gets widened
// Wrap in an outer-level conditional type to allow distribution over props that are unions
type Defaultize<P, D> = P extends any ? string extends keyof P ? P
:
& PickU<P, Exclude<keyof P, keyof D>>
& Partial<PickU<P, Extract<keyof P, keyof D>>>
& Partial<PickU<D, Exclude<keyof D, keyof P>>>
: never;
type ReactDefaultizedProps<C, P> = C extends { defaultProps: infer D } ? Defaultize<P, D> : P;
type MakeAttrsOptional<
C extends string | React.ComponentType<any>,
O extends object,
A extends keyof P,
P = React.ComponentPropsWithRef<C extends IntrinsicElementsKeys | React.ComponentType<any> ? C : never>,
> =
// Distribute unions early to avoid quadratic expansion
P extends any ? OmitU<ReactDefaultizedProps<C, P> & O, A> & Partial<PickU<P & O, A>> : never;
export type StyledComponentProps<
// The Component from whose props are derived
C extends string | React.ComponentType<any>,
// The Theme from the current context
T extends object,
// The other props added by the template
O extends object,
// The props that are made optional by .attrs
A extends keyof any,
// The Component passed with "forwardedAs" prop
FAsC extends string | React.ComponentType<any> = C,
> =
// Distribute O if O is a union type
O extends object ? WithOptionalTheme<
MakeAttrsOptional<C, O, A> & MakeAttrsOptional<FAsC, O, A>,
T
>
: never;
type StyledComponentPropsWithAs<
C extends string | React.ComponentType<any>,
T extends object,
O extends object,
A extends keyof any,
AsC extends string | React.ComponentType<any> = C,
FAsC extends string | React.ComponentType<any> = C,
> = StyledComponentProps<C, T, O, A, FAsC> & { as?: AsC | undefined; forwardedAs?: FAsC | undefined };
export type FalseyValue = undefined | null | false;
export type Interpolation<P> = InterpolationValue | InterpolationFunction<P> | FlattenInterpolation<P>;
// cannot be made a self-referential interface, breaks WithPropNested
// see https://github.com/microsoft/TypeScript/issues/34796
export type FlattenInterpolation<P> = ReadonlyArray<Interpolation<P>>;
export type InterpolationValue = string | number | FalseyValue | Keyframes | StyledComponentInterpolation | CSSObject;
export type SimpleInterpolation = InterpolationValue | FlattenSimpleInterpolation;
export type FlattenSimpleInterpolation = readonly SimpleInterpolation[];
export type InterpolationFunction<P> = (props: P) => Interpolation<P>;
type Attrs<P, A extends Partial<P>, T> = ((props: ThemedStyledProps<P, T>) => A) | A;
export type ThemedGlobalStyledClassProps<P extends { theme?: T | undefined }, T> = WithOptionalTheme<P, T> & {
suppressMultiMountWarning?: boolean | undefined;
};
export interface GlobalStyleComponent<P extends { theme?: T | undefined }, T>
extends React.ComponentClass<ThemedGlobalStyledClassProps<P, T>>
{}
// remove the call signature from StyledComponent so Interpolation can still infer InterpolationFunction
type StyledComponentInterpolation =
| PickU<StyledComponentBase<any, any, any, any>, keyof StyledComponentBase<any, any>>
| PickU<StyledComponentBase<any, any, any>, keyof StyledComponentBase<any, any>>;
// abuse Pick to strip the call signature from ForwardRefExoticComponent
type ForwardRefExoticBase<P> = PickU<React.ForwardRefExoticComponent<P>, keyof React.ForwardRefExoticComponent<any>>;
// Config to be used with withConfig
export interface StyledConfig<O extends object = {}> {
// TODO: Add all types from the original StyledComponentWrapperProperties
componentId?: string;
displayName?: string;
shouldForwardProp?: ((prop: keyof O, defaultValidatorFn: (prop: keyof O) => boolean) => boolean) | undefined;
}
// extracts React defaultProps
type ReactDefaultProps<C> = C extends { defaultProps: infer D } ? D : never;
// any doesn't count as assignable to never in the extends clause, and we default A to never
export type AnyStyledComponent = StyledComponent<any, any, any, any> | StyledComponent<any, any, any>;
export type StyledComponent<
C extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>,
T extends object,
O extends object = {},
A extends keyof any = never,
> = // the "string" allows this to be used as an object key
// I really want to avoid this if possible but it's the only way to use nesting with object styles...
& string
& StyledComponentBase<C, T, O, A>
& hoistNonReactStatics.NonReactStatics<C extends React.ComponentType<any> ? C : never>;
export interface StyledComponentBase<
C extends string | React.ComponentType<any>,
T extends object,
O extends object = {},
A extends keyof any = never,
> extends ForwardRefExoticBase<StyledComponentProps<C, T, O, A>> {
// add our own fake call signature to implement the polymorphic 'as' prop
(
props: StyledComponentProps<C, T, O, A> & { as?: never | undefined; forwardedAs?: never | undefined },
): React.ReactElement<
StyledComponentProps<C, T, O, A>
>;
<AsC extends string | React.ComponentType<any> = C, FAsC extends string | React.ComponentType<any> = AsC>(
props: StyledComponentPropsWithAs<AsC, T, O, A, AsC, FAsC>,
): React.ReactElement<StyledComponentPropsWithAs<AsC, T, O, A, AsC, FAsC>>;
withComponent<WithC extends AnyStyledComponent>(
component: WithC,
): StyledComponent<
StyledComponentInnerComponent<WithC>,
T,
O & StyledComponentInnerOtherProps<WithC>,
A | StyledComponentInnerAttrs<WithC>
>;
withComponent<WithC extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>>(
component: WithC,
): StyledComponent<WithC, T, O, A>;
}
export interface ThemedStyledFunctionBase<
C extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>,
T extends object,
O extends object = {},
A extends keyof any = never,
> {
(first: TemplateStringsArray): StyledComponent<C, T, O, A>;
(
first:
| TemplateStringsArray
| CSSObject
| InterpolationFunction<ThemedStyledProps<StyledComponentPropsWithRef<C> & O, T>>,
...rest: Array<Interpolation<ThemedStyledProps<StyledComponentPropsWithRef<C> & O, T>>>
): StyledComponent<C, T, O, A>;
<U extends object>(
first:
| TemplateStringsArray
| CSSObject
| InterpolationFunction<ThemedStyledProps<StyledComponentPropsWithRef<C> & O & U, T>>,
...rest: Array<Interpolation<ThemedStyledProps<StyledComponentPropsWithRef<C> & O & U, T>>>
): StyledComponent<C, T, O & U, A>;
}
export interface ThemedStyledFunction<
C extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>,
T extends object,
O extends object = {},
A extends keyof any = never,
> extends ThemedStyledFunctionBase<C, T, O, A> {
// Fun thing: 'attrs' can also provide a polymorphic 'as' prop
// My head already hurts enough so maybe later...
attrs<
U,
NewA extends Partial<StyledComponentPropsWithRef<C> & U> & {
[others: string]: any;
} = {},
>(
attrs: Attrs<StyledComponentPropsWithRef<C> & U, NewA, T>,
): ThemedStyledFunction<C, T, O & NewA, A | keyof NewA>;
withConfig: <Props extends O = O>(
config: StyledConfig<StyledComponentPropsWithRef<C> & Props>,
) => ThemedStyledFunction<C, T, Props, A>;
}
export type StyledFunction<C extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>> =
ThemedStyledFunction<
C,
any
>;
type ThemedStyledComponentFactories<T extends object> = {
[TTag in keyof React.JSX.IntrinsicElements]: ThemedStyledFunction<TTag, T>;
};
export type StyledComponentInnerComponent<C extends React.ComponentType<any>> = C extends StyledComponent<
infer I,
any,
any,
any
> ? I
: C extends StyledComponent<infer I, any, any> ? I
: C;
export type StyledComponentPropsWithRef<
C extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>,
> = C extends AnyStyledComponent ? React.ComponentPropsWithRef<StyledComponentInnerComponent<C>>
: React.ComponentPropsWithRef<C>;
export type StyledComponentInnerOtherProps<C extends AnyStyledComponent> = C extends StyledComponent<
any,
any,
infer O,
any
> ? O
: C extends StyledComponent<any, any, infer O> ? O
: never;
export type StyledComponentInnerAttrs<C extends AnyStyledComponent> = C extends StyledComponent<any, any, any, infer A>
? A
: never;
export interface ThemedBaseStyledInterface<T extends object> extends ThemedStyledComponentFactories<T> {
<C extends AnyStyledComponent>(component: C): ThemedStyledFunction<
StyledComponentInnerComponent<C>,
T,
StyledComponentInnerOtherProps<C>,
StyledComponentInnerAttrs<C>
>;
<C extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>>(
// unfortunately using a conditional type to validate that it can receive a `theme?: Theme`
// causes tests to fail in TS 3.1
component: C,
): ThemedStyledFunction<C, T>;
}
export type ThemedStyledInterface<T extends object> = ThemedBaseStyledInterface<AnyIfEmpty<T>>;
export type StyledInterface = ThemedStyledInterface<DefaultTheme>;
export interface BaseThemedCssFunction<T extends object> {
(first: TemplateStringsArray | CSSObject, ...interpolations: SimpleInterpolation[]): FlattenSimpleInterpolation;
(
first: TemplateStringsArray | CSSObject | InterpolationFunction<ThemedStyledProps<{}, T>>,
...interpolations: Array<Interpolation<ThemedStyledProps<{}, T>>>
): FlattenInterpolation<ThemedStyledProps<{}, T>>;
<P extends object>(
first: TemplateStringsArray | CSSObject | InterpolationFunction<ThemedStyledProps<P, T>>,
...interpolations: Array<Interpolation<ThemedStyledProps<P, T>>>
): FlattenInterpolation<ThemedStyledProps<P, T>>;
}
export type ThemedCssFunction<T extends object> = BaseThemedCssFunction<AnyIfEmpty<T>>;
// Helper type operators
// Pick that distributes over union types
export type PickU<T, K extends keyof T> = T extends any ? { [P in K]: T[P] } : never;
export type OmitU<T, K extends keyof T> = T extends any ? PickU<T, Exclude<keyof T, K>> : never;
type WithOptionalTheme<P extends { theme?: T | undefined }, T> = OmitU<P, "theme"> & {
theme?: T | undefined;
};
type AnyIfEmpty<T extends object> = keyof T extends never ? any : T;
export interface ThemedStyledComponentsModule<T extends object, U extends object = T> {
default: ThemedStyledInterface<T>;
css: ThemedCssFunction<T>;
// unfortunately keyframes can't interpolate props from the theme
keyframes: (strings: TemplateStringsArray | CSSKeyframes, ...interpolations: SimpleInterpolation[]) => Keyframes;
createGlobalStyle: <P extends object = {}>(
first: TemplateStringsArray | CSSObject | InterpolationFunction<ThemedStyledProps<P, T>>,
...interpolations: Array<Interpolation<ThemedStyledProps<P, T>>>
) => GlobalStyleComponent<P, T>;
withTheme: BaseWithThemeFnInterface<T>;
ThemeProvider: BaseThemeProviderComponent<T, U>;
ThemeConsumer: React.Consumer<T>;
ThemeContext: React.Context<T>;
useTheme(): T;
// This could be made to assert `target is StyledComponent<any, T>` instead, but that feels not type safe
isStyledComponent: typeof isStyledComponent;
ServerStyleSheet: typeof ServerStyleSheet;
StyleSheetManager: typeof StyleSheetManager;
}
declare const styled: StyledInterface;
export const css: ThemedCssFunction<DefaultTheme>;
export type BaseWithThemeFnInterface<T extends object> = <C extends React.ComponentType<any>>(
// this check is roundabout because the extends clause above would
// not allow any component that accepts _more_ than theme as a prop
component: React.ComponentProps<C> extends { theme?: T | undefined } ? C : never,
) => React.ForwardRefExoticComponent<
WithOptionalTheme<React.JSX.LibraryManagedAttributes<C, React.ComponentPropsWithRef<C>>, T>
>;
export type WithThemeFnInterface<T extends object> = BaseWithThemeFnInterface<AnyIfEmpty<T>>;
export const withTheme: WithThemeFnInterface<DefaultTheme>;
export function useTheme(): DefaultTheme;
/**
* This interface can be augmented by users to add types to `styled-components`' default theme
* without needing to reexport `ThemedStyledComponentsModule`.
*/
// Unfortunately, there is no way to write tests for this
// as any augmentation will break the tests for the default case (not augmented).
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface DefaultTheme {}
export interface ThemeProviderProps<T extends object, U extends object = T> {
children?: React.ReactNode | undefined;
theme: T | ((theme: U) => T);
}
export type BaseThemeProviderComponent<T extends object, U extends object = T> = React.ComponentClass<
ThemeProviderProps<T, U>
>;
export type ThemeProviderComponent<T extends object, U extends object = T> = BaseThemeProviderComponent<
AnyIfEmpty<T>,
AnyIfEmpty<U>
>;
export const ThemeProvider: ThemeProviderComponent<AnyIfEmpty<DefaultTheme>>;
// NOTE: this technically starts as undefined, but allowing undefined is unhelpful when used correctly
export const ThemeContext: React.Context<AnyIfEmpty<DefaultTheme>>;
export const ThemeConsumer: typeof ThemeContext["Consumer"];
export interface Keyframes {
getName(): string;
}
export function keyframes(
strings: TemplateStringsArray | CSSKeyframes,
...interpolations: SimpleInterpolation[]
): Keyframes;
export function createGlobalStyle<P extends object = {}>(
first: TemplateStringsArray | CSSObject | InterpolationFunction<ThemedStyledProps<P, DefaultTheme>>,
...interpolations: Array<Interpolation<ThemedStyledProps<P, DefaultTheme>>>
): GlobalStyleComponent<P, DefaultTheme>;
export function isStyledComponent(target: any): target is StyledComponent<any, any>;
export class ServerStyleSheet {
collectStyles(tree: React.ReactNode): React.ReactElement<{ sheet: ServerStyleSheet }>;
getStyleTags(): string;
getStyleElement(): Array<React.ReactElement<{}>>;
interleaveWithNodeStream(readableStream: NodeJS.ReadableStream): NodeJS.ReadableStream;
readonly instance: this;
seal(): void;
clearTag(): void;
}
export type StylisPlugin = (
context: number,
selector: string[],
parent: string[],
content: string,
line: number,
column: number,
length: number,
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
) => string | void;
export interface StyleSheetManagerProps {
children?: React.ReactNode;
disableCSSOMInjection?: boolean | undefined;
disableVendorPrefixes?: boolean | undefined;
stylisPlugins?: StylisPlugin[] | undefined;
sheet?: ServerStyleSheet | undefined;
target?: HTMLElement | ShadowRoot | undefined;
}
export class StyleSheetManager extends React.Component<StyleSheetManagerProps> {}
/**
* 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.
*
* You can load a default declaration by using writing this special import from
* a typescript file. This module does not exist in reality, which is why the {} is important:
*
* ```ts
* import {} from 'styled-components/cssprop'
* ```
*
* Or you can declare your own module augmentation, which allows you to specify the type of Theme:
*
* ```ts
* import { CSSProp } from 'styled-components'
*
* interface MyTheme {}
*
* declare module 'react' {
* interface Attributes {
* css?: CSSProp<MyTheme>
* }
* }
* ```
*/
// ONLY string literals and inline invocations of css`` are supported, anything else crashes the plugin
export type CSSProp<T = AnyIfEmpty<DefaultTheme>> = string | CSSObject | FlattenInterpolation<ThemeProps<T>>;
export default styled;