UNPKG

17.4 kBTypeScriptView Raw
1// forward declarations
2declare global {
3 namespace NodeJS {
4 // eslint-disable-next-line @typescript-eslint/no-empty-interface
5 interface ReadableStream {}
6 }
7 // eslint-disable-next-line @typescript-eslint/no-empty-interface
8 interface ShadowRoot {}
9}
10
11import * as CSS from "csstype";
12import * as hoistNonReactStatics from "hoist-non-react-statics";
13import * as React from "react";
14
15export type CSSProperties = CSS.Properties<string | number>;
16
17export type CSSPseudos = { [K in CSS.Pseudos]?: CSSObject };
18
19export interface CSSObject extends CSSProperties, CSSPseudos {
20 [key: string]: CSSObject | string | number | undefined;
21}
22
23export type CSSKeyframes = object & { [key: string]: CSSObject };
24
25export interface ThemeProps<T> {
26 theme: T;
27}
28
29export type ThemedStyledProps<P, T> = P & ThemeProps<T>;
30export type StyledProps<P> = ThemedStyledProps<P, AnyIfEmpty<DefaultTheme>>;
31export type IntrinsicElementsKeys = keyof React.JSX.IntrinsicElements;
32
33// Any prop that has a default prop becomes optional, but its type is unchanged
34// Undeclared default props are augmented into the resulting allowable attributes
35// If declared props have indexed properties, ignore default props entirely as keyof gets widened
36// Wrap in an outer-level conditional type to allow distribution over props that are unions
37type Defaultize<P, D> = P extends any ? string extends keyof P ? P
38 :
39 & PickU<P, Exclude<keyof P, keyof D>>
40 & Partial<PickU<P, Extract<keyof P, keyof D>>>
41 & Partial<PickU<D, Exclude<keyof D, keyof P>>>
42 : never;
43
44type ReactDefaultizedProps<C, P> = C extends { defaultProps: infer D } ? Defaultize<P, D> : P;
45
46type MakeAttrsOptional<
47 C extends string | React.ComponentType<any>,
48 O extends object,
49 A extends keyof P,
50 P = React.ComponentPropsWithRef<C extends IntrinsicElementsKeys | React.ComponentType<any> ? C : never>,
51> =
52 // Distribute unions early to avoid quadratic expansion
53 P extends any ? OmitU<ReactDefaultizedProps<C, P> & O, A> & Partial<PickU<P & O, A>> : never;
54
55export type StyledComponentProps<
56 // The Component from whose props are derived
57 C extends string | React.ComponentType<any>,
58 // The Theme from the current context
59 T extends object,
60 // The other props added by the template
61 O extends object,
62 // The props that are made optional by .attrs
63 A extends keyof any,
64 // The Component passed with "forwardedAs" prop
65 FAsC extends string | React.ComponentType<any> = C,
66> =
67 // Distribute O if O is a union type
68 O extends object ? WithOptionalTheme<
69 MakeAttrsOptional<C, O, A> & MakeAttrsOptional<FAsC, O, A>,
70 T
71 >
72 : never;
73
74type StyledComponentPropsWithAs<
75 C extends string | React.ComponentType<any>,
76 T extends object,
77 O extends object,
78 A extends keyof any,
79 AsC extends string | React.ComponentType<any> = C,
80 FAsC extends string | React.ComponentType<any> = C,
81> = StyledComponentProps<C, T, O, A, FAsC> & { as?: AsC | undefined; forwardedAs?: FAsC | undefined };
82
83export type FalseyValue = undefined | null | false;
84export type Interpolation<P> = InterpolationValue | InterpolationFunction<P> | FlattenInterpolation<P>;
85// cannot be made a self-referential interface, breaks WithPropNested
86// see https://github.com/microsoft/TypeScript/issues/34796
87export type FlattenInterpolation<P> = ReadonlyArray<Interpolation<P>>;
88export type InterpolationValue = string | number | FalseyValue | Keyframes | StyledComponentInterpolation | CSSObject;
89export type SimpleInterpolation = InterpolationValue | FlattenSimpleInterpolation;
90export type FlattenSimpleInterpolation = readonly SimpleInterpolation[];
91
92export type InterpolationFunction<P> = (props: P) => Interpolation<P>;
93
94type Attrs<P, A extends Partial<P>, T> = ((props: ThemedStyledProps<P, T>) => A) | A;
95
96export type ThemedGlobalStyledClassProps<P extends { theme?: T | undefined }, T> = WithOptionalTheme<P, T> & {
97 suppressMultiMountWarning?: boolean | undefined;
98};
99
100export interface GlobalStyleComponent<P extends { theme?: T | undefined }, T>
101 extends React.ComponentClass<ThemedGlobalStyledClassProps<P, T>>
102{}
103
104// remove the call signature from StyledComponent so Interpolation can still infer InterpolationFunction
105type StyledComponentInterpolation =
106 | PickU<StyledComponentBase<any, any, any, any>, keyof StyledComponentBase<any, any>>
107 | PickU<StyledComponentBase<any, any, any>, keyof StyledComponentBase<any, any>>;
108
109// abuse Pick to strip the call signature from ForwardRefExoticComponent
110type ForwardRefExoticBase<P> = PickU<React.ForwardRefExoticComponent<P>, keyof React.ForwardRefExoticComponent<any>>;
111
112// Config to be used with withConfig
113export interface StyledConfig<O extends object = {}> {
114 // TODO: Add all types from the original StyledComponentWrapperProperties
115 componentId?: string;
116 displayName?: string;
117 shouldForwardProp?: ((prop: keyof O, defaultValidatorFn: (prop: keyof O) => boolean) => boolean) | undefined;
118}
119
120// extracts React defaultProps
121type ReactDefaultProps<C> = C extends { defaultProps: infer D } ? D : never;
122
123// any doesn't count as assignable to never in the extends clause, and we default A to never
124export type AnyStyledComponent = StyledComponent<any, any, any, any> | StyledComponent<any, any, any>;
125
126export type StyledComponent<
127 C extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>,
128 T extends object,
129 O extends object = {},
130 A extends keyof any = never,
131> = // the "string" allows this to be used as an object key
132 // I really want to avoid this if possible but it's the only way to use nesting with object styles...
133 & string
134 & StyledComponentBase<C, T, O, A>
135 & hoistNonReactStatics.NonReactStatics<C extends React.ComponentType<any> ? C : never>;
136
137export interface StyledComponentBase<
138 C extends string | React.ComponentType<any>,
139 T extends object,
140 O extends object = {},
141 A extends keyof any = never,
142> extends ForwardRefExoticBase<StyledComponentProps<C, T, O, A>> {
143 // add our own fake call signature to implement the polymorphic 'as' prop
144 (
145 props: StyledComponentProps<C, T, O, A> & { as?: never | undefined; forwardedAs?: never | undefined },
146 ): React.ReactElement<
147 StyledComponentProps<C, T, O, A>
148 >;
149 <AsC extends string | React.ComponentType<any> = C, FAsC extends string | React.ComponentType<any> = AsC>(
150 props: StyledComponentPropsWithAs<AsC, T, O, A, AsC, FAsC>,
151 ): React.ReactElement<StyledComponentPropsWithAs<AsC, T, O, A, AsC, FAsC>>;
152
153 withComponent<WithC extends AnyStyledComponent>(
154 component: WithC,
155 ): StyledComponent<
156 StyledComponentInnerComponent<WithC>,
157 T,
158 O & StyledComponentInnerOtherProps<WithC>,
159 A | StyledComponentInnerAttrs<WithC>
160 >;
161 withComponent<WithC extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>>(
162 component: WithC,
163 ): StyledComponent<WithC, T, O, A>;
164}
165
166export interface ThemedStyledFunctionBase<
167 C extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>,
168 T extends object,
169 O extends object = {},
170 A extends keyof any = never,
171> {
172 (first: TemplateStringsArray): StyledComponent<C, T, O, A>;
173 (
174 first:
175 | TemplateStringsArray
176 | CSSObject
177 | InterpolationFunction<ThemedStyledProps<StyledComponentPropsWithRef<C> & O, T>>,
178 ...rest: Array<Interpolation<ThemedStyledProps<StyledComponentPropsWithRef<C> & O, T>>>
179 ): StyledComponent<C, T, O, A>;
180 <U extends object>(
181 first:
182 | TemplateStringsArray
183 | CSSObject
184 | InterpolationFunction<ThemedStyledProps<StyledComponentPropsWithRef<C> & O & U, T>>,
185 ...rest: Array<Interpolation<ThemedStyledProps<StyledComponentPropsWithRef<C> & O & U, T>>>
186 ): StyledComponent<C, T, O & U, A>;
187}
188
189export interface ThemedStyledFunction<
190 C extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>,
191 T extends object,
192 O extends object = {},
193 A extends keyof any = never,
194> extends ThemedStyledFunctionBase<C, T, O, A> {
195 // Fun thing: 'attrs' can also provide a polymorphic 'as' prop
196 // My head already hurts enough so maybe later...
197 attrs<
198 U,
199 NewA extends Partial<StyledComponentPropsWithRef<C> & U> & {
200 [others: string]: any;
201 } = {},
202 >(
203 attrs: Attrs<StyledComponentPropsWithRef<C> & U, NewA, T>,
204 ): ThemedStyledFunction<C, T, O & NewA, A | keyof NewA>;
205
206 withConfig: <Props extends O = O>(
207 config: StyledConfig<StyledComponentPropsWithRef<C> & Props>,
208 ) => ThemedStyledFunction<C, T, Props, A>;
209}
210
211export type StyledFunction<C extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>> =
212 ThemedStyledFunction<
213 C,
214 any
215 >;
216
217type ThemedStyledComponentFactories<T extends object> = {
218 [TTag in keyof React.JSX.IntrinsicElements]: ThemedStyledFunction<TTag, T>;
219};
220
221export type StyledComponentInnerComponent<C extends React.ComponentType<any>> = C extends StyledComponent<
222 infer I,
223 any,
224 any,
225 any
226> ? I
227 : C extends StyledComponent<infer I, any, any> ? I
228 : C;
229export type StyledComponentPropsWithRef<
230 C extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>,
231> = C extends AnyStyledComponent ? React.ComponentPropsWithRef<StyledComponentInnerComponent<C>>
232 : React.ComponentPropsWithRef<C>;
233export type StyledComponentInnerOtherProps<C extends AnyStyledComponent> = C extends StyledComponent<
234 any,
235 any,
236 infer O,
237 any
238> ? O
239 : C extends StyledComponent<any, any, infer O> ? O
240 : never;
241export type StyledComponentInnerAttrs<C extends AnyStyledComponent> = C extends StyledComponent<any, any, any, infer A>
242 ? A
243 : never;
244
245export interface ThemedBaseStyledInterface<T extends object> extends ThemedStyledComponentFactories<T> {
246 <C extends AnyStyledComponent>(component: C): ThemedStyledFunction<
247 StyledComponentInnerComponent<C>,
248 T,
249 StyledComponentInnerOtherProps<C>,
250 StyledComponentInnerAttrs<C>
251 >;
252 <C extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>>(
253 // unfortunately using a conditional type to validate that it can receive a `theme?: Theme`
254 // causes tests to fail in TS 3.1
255 component: C,
256 ): ThemedStyledFunction<C, T>;
257}
258
259export type ThemedStyledInterface<T extends object> = ThemedBaseStyledInterface<AnyIfEmpty<T>>;
260export type StyledInterface = ThemedStyledInterface<DefaultTheme>;
261
262export interface BaseThemedCssFunction<T extends object> {
263 (first: TemplateStringsArray | CSSObject, ...interpolations: SimpleInterpolation[]): FlattenSimpleInterpolation;
264 (
265 first: TemplateStringsArray | CSSObject | InterpolationFunction<ThemedStyledProps<{}, T>>,
266 ...interpolations: Array<Interpolation<ThemedStyledProps<{}, T>>>
267 ): FlattenInterpolation<ThemedStyledProps<{}, T>>;
268 <P extends object>(
269 first: TemplateStringsArray | CSSObject | InterpolationFunction<ThemedStyledProps<P, T>>,
270 ...interpolations: Array<Interpolation<ThemedStyledProps<P, T>>>
271 ): FlattenInterpolation<ThemedStyledProps<P, T>>;
272}
273
274export type ThemedCssFunction<T extends object> = BaseThemedCssFunction<AnyIfEmpty<T>>;
275
276// Helper type operators
277// Pick that distributes over union types
278export type PickU<T, K extends keyof T> = T extends any ? { [P in K]: T[P] } : never;
279export type OmitU<T, K extends keyof T> = T extends any ? PickU<T, Exclude<keyof T, K>> : never;
280type WithOptionalTheme<P extends { theme?: T | undefined }, T> = OmitU<P, "theme"> & {
281 theme?: T | undefined;
282};
283type AnyIfEmpty<T extends object> = keyof T extends never ? any : T;
284
285export interface ThemedStyledComponentsModule<T extends object, U extends object = T> {
286 default: ThemedStyledInterface<T>;
287
288 css: ThemedCssFunction<T>;
289
290 // unfortunately keyframes can't interpolate props from the theme
291 keyframes: (strings: TemplateStringsArray | CSSKeyframes, ...interpolations: SimpleInterpolation[]) => Keyframes;
292
293 createGlobalStyle: <P extends object = {}>(
294 first: TemplateStringsArray | CSSObject | InterpolationFunction<ThemedStyledProps<P, T>>,
295 ...interpolations: Array<Interpolation<ThemedStyledProps<P, T>>>
296 ) => GlobalStyleComponent<P, T>;
297
298 withTheme: BaseWithThemeFnInterface<T>;
299 ThemeProvider: BaseThemeProviderComponent<T, U>;
300 ThemeConsumer: React.Consumer<T>;
301 ThemeContext: React.Context<T>;
302 useTheme(): T;
303
304 // This could be made to assert `target is StyledComponent<any, T>` instead, but that feels not type safe
305 isStyledComponent: typeof isStyledComponent;
306
307 ServerStyleSheet: typeof ServerStyleSheet;
308 StyleSheetManager: typeof StyleSheetManager;
309}
310
311declare const styled: StyledInterface;
312
313export const css: ThemedCssFunction<DefaultTheme>;
314
315export type BaseWithThemeFnInterface<T extends object> = <C extends React.ComponentType<any>>(
316 // this check is roundabout because the extends clause above would
317 // not allow any component that accepts _more_ than theme as a prop
318 component: React.ComponentProps<C> extends { theme?: T | undefined } ? C : never,
319) => React.ForwardRefExoticComponent<
320 WithOptionalTheme<React.JSX.LibraryManagedAttributes<C, React.ComponentPropsWithRef<C>>, T>
321>;
322export type WithThemeFnInterface<T extends object> = BaseWithThemeFnInterface<AnyIfEmpty<T>>;
323export const withTheme: WithThemeFnInterface<DefaultTheme>;
324
325export function useTheme(): DefaultTheme;
326
327/**
328 * This interface can be augmented by users to add types to `styled-components`' default theme
329 * without needing to reexport `ThemedStyledComponentsModule`.
330 */
331// Unfortunately, there is no way to write tests for this
332// as any augmentation will break the tests for the default case (not augmented).
333// eslint-disable-next-line @typescript-eslint/no-empty-interface
334export interface DefaultTheme {}
335
336export interface ThemeProviderProps<T extends object, U extends object = T> {
337 children?: React.ReactNode | undefined;
338 theme: T | ((theme: U) => T);
339}
340export type BaseThemeProviderComponent<T extends object, U extends object = T> = React.ComponentClass<
341 ThemeProviderProps<T, U>
342>;
343export type ThemeProviderComponent<T extends object, U extends object = T> = BaseThemeProviderComponent<
344 AnyIfEmpty<T>,
345 AnyIfEmpty<U>
346>;
347export const ThemeProvider: ThemeProviderComponent<AnyIfEmpty<DefaultTheme>>;
348// NOTE: this technically starts as undefined, but allowing undefined is unhelpful when used correctly
349export const ThemeContext: React.Context<AnyIfEmpty<DefaultTheme>>;
350export const ThemeConsumer: typeof ThemeContext["Consumer"];
351
352export interface Keyframes {
353 getName(): string;
354}
355
356export function keyframes(
357 strings: TemplateStringsArray | CSSKeyframes,
358 ...interpolations: SimpleInterpolation[]
359): Keyframes;
360
361export function createGlobalStyle<P extends object = {}>(
362 first: TemplateStringsArray | CSSObject | InterpolationFunction<ThemedStyledProps<P, DefaultTheme>>,
363 ...interpolations: Array<Interpolation<ThemedStyledProps<P, DefaultTheme>>>
364): GlobalStyleComponent<P, DefaultTheme>;
365
366export function isStyledComponent(target: any): target is StyledComponent<any, any>;
367
368export class ServerStyleSheet {
369 collectStyles(tree: React.ReactNode): React.ReactElement<{ sheet: ServerStyleSheet }>;
370
371 getStyleTags(): string;
372 getStyleElement(): Array<React.ReactElement<{}>>;
373 interleaveWithNodeStream(readableStream: NodeJS.ReadableStream): NodeJS.ReadableStream;
374 readonly instance: this;
375 seal(): void;
376 clearTag(): void;
377}
378
379export type StylisPlugin = (
380 context: number,
381 selector: string[],
382 parent: string[],
383 content: string,
384 line: number,
385 column: number,
386 length: number,
387 // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
388) => string | void;
389
390export interface StyleSheetManagerProps {
391 children?: React.ReactNode;
392 disableCSSOMInjection?: boolean | undefined;
393 disableVendorPrefixes?: boolean | undefined;
394 stylisPlugins?: StylisPlugin[] | undefined;
395 sheet?: ServerStyleSheet | undefined;
396 target?: HTMLElement | ShadowRoot | undefined;
397}
398
399export class StyleSheetManager extends React.Component<StyleSheetManagerProps> {}
400
401/**
402 * The CSS prop is not declared by default in the types as it would cause 'css' to be present
403 * on the types of anything that uses styled-components indirectly, even if they do not use the
404 * babel plugin.
405 *
406 * You can load a default declaration by using writing this special import from
407 * a typescript file. This module does not exist in reality, which is why the {} is important:
408 *
409 * ```ts
410 * import {} from 'styled-components/cssprop'
411 * ```
412 *
413 * Or you can declare your own module augmentation, which allows you to specify the type of Theme:
414 *
415 * ```ts
416 * import { CSSProp } from 'styled-components'
417 *
418 * interface MyTheme {}
419 *
420 * declare module 'react' {
421 * interface Attributes {
422 * css?: CSSProp<MyTheme>
423 * }
424 * }
425 * ```
426 */
427// ONLY string literals and inline invocations of css`` are supported, anything else crashes the plugin
428export type CSSProp<T = AnyIfEmpty<DefaultTheme>> = string | CSSObject | FlattenInterpolation<ThemeProps<T>>;
429
430export default styled;