diff --git a/types/styled-components/index.d.ts b/types/styled-components/index.d.ts index c89b50c86d..2ab1970d44 100644 --- a/types/styled-components/index.d.ts +++ b/types/styled-components/index.d.ts @@ -6,6 +6,7 @@ // Jessica Franco // Jason Killian // Sebastian Silbermann +// David Ruisinger // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.9 diff --git a/types/styled-components/native.d.ts b/types/styled-components/native.d.ts index 0c1c520270..189e284525 100644 --- a/types/styled-components/native.d.ts +++ b/types/styled-components/native.d.ts @@ -2,24 +2,28 @@ import * as ReactNative from 'react-native'; import * as React from 'react'; export { - DefaultTheme, css, + DefaultTheme, isStyledComponent, - ThemeProps, - ThemeProvider, ThemeConsumer, ThemeContext, + ThemeProps, + ThemeProvider, withTheme, } from './index'; import { AnyStyledComponent, + DefaultTheme, + isStyledComponent, + StyledComponentInnerAttrs, StyledComponentInnerComponent, StyledComponentInnerOtherProps, - StyledComponentInnerAttrs, + ThemedCssFunction, ThemedStyledFunction, ThemedStyledInterface, - DefaultTheme + ThemeProviderComponent, + WithThemeFnInterface } from './index'; type AnyIfEmpty = keyof T extends never ? any : T; @@ -211,6 +215,23 @@ export interface ReactNativeStyledInterface extends ReactNativ >; } +export interface ReactNativeThemedStyledComponentsModule< + T extends object, + U extends object = T +> { + default: ReactNativeStyledInterface; + + css: ThemedCssFunction; + + withTheme: WithThemeFnInterface; + ThemeProvider: ThemeProviderComponent; + ThemeConsumer: React.Consumer; + ThemeContext: React.Context; + + // This could be made to assert `target is StyledComponent` instead, but that feels not type safe + isStyledComponent: typeof isStyledComponent; +} + declare const styled: ReactNativeStyledInterface; export default styled; diff --git a/types/styled-components/test/native.tsx b/types/styled-components/test/native.tsx index 66b0f975ce..da2ba83390 100644 --- a/types/styled-components/test/native.tsx +++ b/types/styled-components/test/native.tsx @@ -9,6 +9,7 @@ import styled, { ThemeProvider, withTheme, ThemeConsumer, + ReactNativeThemedStyledComponentsModule, } from "styled-components/native"; import {} from "styled-components/cssprop"; @@ -57,3 +58,141 @@ const TomatoButton = styled(MyButton)` // needs name prop, but not theme prop const tomatoElement = ; + +async function typedThemes() { + const theme = { + color: "green" + }; + + // abuse "await import(...)" to be able to reference the styled-components namespace + // without actually doing a top level namespace import + const { + default: styled, + css, + ThemeProvider, + ThemeConsumer + } = (await import("styled-components/native")) as any as ReactNativeThemedStyledComponentsModule< + typeof theme + >; + + const ThemedView = styled.View` + background: ${props => { + // $ExpectType string + props.theme.color; + // $ExpectType string | undefined + props.testID; + return props.theme.color; + }}; + `; + const ThemedView2 = styled.View(props => { + // $ExpectType string + props.theme.color; + // $ExpectType string | undefined + props.testID; + + return { + background: props.theme.color + }; + }); + const ThemedView3 = styled.View(props => { + // $ExpectType string + props.theme.color; + // $ExpectType string | undefined + props.testID; + + return css` + background: ${props.theme.color}; + `; +}); +const themedCss = css` + background: ${props => { + // $ExpectType string + props.theme.color; + // $ExpectType "theme" + type Keys = keyof typeof props; + return props.theme.color; + }}; +`; +// can't use a FlattenInterpolation as the first argument, would make broken css +// $ExpectError +const ThemedView4 = styled.View(themedCss); + +const themedCssWithNesting = css(props => ({ + color: props.theme.color, + [ThemedView3]: { + color: "green" + } +})); + +return ( + + <> + + + + + {theme => { + // $ExpectType string + theme.color; + return theme.color; + }} + + + + ); +} + +async function reexportCompatibility() { + const sc = await import("styled-components/native"); + const themed = sc as ReactNativeThemedStyledComponentsModule; + + let { ...scExports } = sc; + let { ...themedExports } = themed; + // both branches must be assignable to each other + if (Math.random()) { + scExports = themedExports; + } else { + themedExports = scExports; + } +} + +async function themeAugmentation() { + interface BaseTheme { + background: string; + } + interface ExtraTheme extends BaseTheme { + accent: string; + } + + const base = (await import("styled-components/native")) as any as ReactNativeThemedStyledComponentsModule< + BaseTheme + >; + const extra = (await import("styled-components/native")) as any as ReactNativeThemedStyledComponentsModule< + ExtraTheme, + BaseTheme + >; + + return ( + + <> + base} // $ExpectError + > + {() => null} + + ({ + ...base, + accent: "blue" + })} + > + {() => null} + + + + ); +}