UNPKG

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