diff --git a/.changeset/long-apes-admire/changes.json b/.changeset/long-apes-admire/changes.json new file mode 100644 index 000000000..c11ee7450 --- /dev/null +++ b/.changeset/long-apes-admire/changes.json @@ -0,0 +1,31 @@ +{ + "releases": [ + { "name": "@emotion/css", "type": "minor" }, + { "name": "emotion-theming", "type": "minor" }, + { "name": "@emotion/serialize", "type": "minor" }, + { "name": "@emotion/styled-base", "type": "minor" }, + { "name": "@emotion/styled", "type": "minor" } + ], + "dependents": [ + { + "name": "babel-plugin-emotion", + "type": "patch", + "dependencies": ["@emotion/serialize"] + }, + { + "name": "@emotion/core", + "type": "patch", + "dependencies": [ + "@emotion/css", + "emotion-theming", + "@emotion/serialize", + "@emotion/styled" + ] + }, + { + "name": "create-emotion", + "type": "patch", + "dependencies": ["@emotion/serialize"] + } + ] +} diff --git a/.changeset/long-apes-admire/changes.md b/.changeset/long-apes-admire/changes.md new file mode 100644 index 000000000..b94aae8c4 --- /dev/null +++ b/.changeset/long-apes-admire/changes.md @@ -0,0 +1,31 @@ +TypeScript types have been restructured. These changes: + +- Reduce build times when using emotion +- In many cases remove the need for manually specifying generic parameters for your emotion components. + +If you encounter build issues after upgrade, try removing any manually specified generic types and let them be inferred. Otherwise refer to the breaking changes list below. + +## Improvements + +- useTheme added to EmotionTheming interface and can now create your own closed variation of withTheme. More information in the docs under the theming section. +- Union types as props are better supported and should be inferred properly +- Build times should be reduced significantly in larger projects. + +## Breaking changes + +- withTheme can now have the Theme type specified when calling it. For example `withTheme(MyComponent)` + + **Breaking change:** Generic argument changed, if you were specifying the ComponentType you will need to remove the generic parameter. Recommend following example setup in the TypeScript docs under theming section + +- `css` function has been restricted to prevent passing of invalid types +- `CreateStyled` functions no longer take a second `ExtraProps` argument. Instead move it to after the create styled call. For example + + `styled(MyComponent)({})` + to + `styled(MyComponent)({})` + +- `StyledComponent` type no longer supports the third generic `Theme` parameter. Instead add the `theme` prop to the first `Props` argument. For example: + + `StyledComponent` + to + `StyledComponent` diff --git a/.circleci/config.yml b/.circleci/config.yml index 7212aaf19..7730d3b32 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: flow: docker: - - image: circleci/node:10.6.0-browsers + - image: circleci/node:10.16.3-browsers working_directory: ~/repo steps: - checkout @@ -25,7 +25,7 @@ jobs: test: docker: - - image: circleci/node:10.6.0 + - image: circleci/node:10.16.3 working_directory: ~/repo steps: - checkout @@ -48,7 +48,7 @@ jobs: test_dist: docker: - - image: circleci/node:10.6.0 + - image: circleci/node:10.16.3 working_directory: ~/repo steps: - checkout @@ -66,7 +66,7 @@ jobs: lint_and_typescript: docker: - - image: circleci/node:10.6.0 + - image: circleci/node:10.16.3 working_directory: ~/repo steps: - checkout @@ -77,6 +77,7 @@ jobs: - v4-dependencies- - run: yarn install --pure-lockfile - run: yarn lint:check + - run: yarn test:typescript workflows: version: 2 diff --git a/docs/typescript.mdx b/docs/typescript.mdx index 91232cd68..7ea0c8a93 100644 --- a/docs/typescript.mdx +++ b/docs/typescript.mdx @@ -39,10 +39,21 @@ To make the css prop work with pure TypeScript (without babel plugin) you need t ```tsx /** @jsx jsx */ -import { css, jsx } from '@emotion/core' +import { jsx } from '@emotion/core' + +
``` As a result you may be not able to use react fragment shorthand syntax - `<>`, but still you can use ``. +This is a limitation of the TypeScript compiler not being able to independently specify jsx pragma and jsxFrag pragma. + +You can still use the css helper and pass the className yourself (ensure you are importing from the `@emotion` package, not `@emotion/core`). + +```tsx +import { css } from '@emotion' + +
+``` ## @emotion/styled @@ -103,28 +114,20 @@ type ImageProps = { width: number } -const Image0 = styled('div')` - width: ${(props: ImageProps) => props.width}; - background: url(${(props: ImageProps) => props.src}) - center center; +// Using a css block +const Image0 = styled.div` + width: ${props => props.width}; + background: url(${props => props.src}) center center; + background-size: contain; +` +const Image0 = styled('div')` + width: ${props => props.width}; + background: url(${props => props.src}) center center; background-size: contain; ` // Or with object styles - -const Image1 = styled('div')( - { - backgroundSize: 'contain' - }, - (props: ImageProps) => ({ - width: props.width, - background: `url(${props.src}) center center` - }) -) - -// Or with a generic type - -const Image2 = styled('div')( +const Image1 = styled('div')( { backgroundSize: 'contain' }, @@ -133,36 +136,28 @@ const Image2 = styled('div')( background: `url(${props.src}) center center` }) ) - -// TS 2.9+ only -const Image3 = styled.div` - width: ${(props: ImageProps) => props.width}; - background: url(${(props: ImageProps) => props.src}) - center center; - background-size: contain; -` ``` -- For TypeScript <2.9, the generic type version only works with object styles due to https://github.com/Microsoft/TypeScript/issues/11947. - ### React Components +Emotion can also style React components and will infer component props as expected. + ```tsx -import React, { SFC } from 'react' +import React, { FC } from 'react' import styled from '@emotion/styled' -type ComponentProps = { +interface ComponentProps { className?: string label: string } -const Component: SFC = ({ +const Component: FC = ({ label, className }) =>
{label}
const StyledComponent0 = styled(Component)` - color: red; + color: ${props => label === 'Important' ? 'red' : 'green'}; ` const StyledComponent1 = styled(Component)({ @@ -171,7 +166,7 @@ const StyledComponent1 = styled(Component)({ const App = () => (
- +
) @@ -180,37 +175,32 @@ const App = () => ( ### Passing props when styling a React component ```tsx -import React, { SFC } from 'react' +import React, { FC } from 'react' import styled from '@emotion/styled' -type ComponentProps = { +interface ComponentProps { className?: string label: string } -const Component: SFC = ({ +const Component: FC = ({ label, className }) =>
{label}
-type StyledComponentProps = { +interface StyledComponentProps { bgColor: string } -const StyledComponent0 = styled(Component)` +const StyledComponent0 = styled(Component)` color: red; - background: ${(props: StyledComponentProps) => - props.bgColor}; + background: ${props => props.label ? props.bgColor : 'white'}; ` - -const StyledComponent1 = styled(Component)< - StyledComponentProps ->( - { - color: 'red' - }, +// or +const StyledComponent1 = styled(Component)( props => ({ - background: props.bgColor + color: 'red' + background: props.label ? props.bgColor : 'white' }) ) @@ -237,6 +227,7 @@ _styled.tsx_ ```tsx import styled, { CreateStyled } from '@emotion/styled' +import * as emotionTheming from 'emotion-theming' type Theme = { color: { @@ -248,6 +239,10 @@ type Theme = { } export default styled as CreateStyled + +// You can also create themed versions of all the other theme helpers and hooks +const { ThemeProvider, withTheme, useTheme } = emotionTheming as emotionTheming.EmotionTheming +export { ThemeProvider, withTheme, useTheme } ``` _Button.tsx_ @@ -263,3 +258,27 @@ const Button = styled('button')` export default Button ``` + +### TypeScript < 2.9 + +For Typescript <2.9, the generic type version only works with object styles due to https://github.com/Microsoft/TypeScript/issues/11947. + +You can work around this by specifying the prop types in your style callback: + +``` ts +const StyledComponent0 = styled(Component)` + color: red; + background: ${(props: StyledComponentProps) => + props.bgColor}; +` +``` + +NOTE: This approach you will have to perform the intersection with the component props yourself to get at the component props + +``` ts +const StyledComponent0 = styled(Component)` + color: red; + background: ${(props: StyledComponentProps & ComponentProps) => + props.bgColor}; +` +``` \ No newline at end of file diff --git a/packages/babel-plugin-emotion/package.json b/packages/babel-plugin-emotion/package.json index 50c2c9ae5..473f75d1a 100644 --- a/packages/babel-plugin-emotion/package.json +++ b/packages/babel-plugin-emotion/package.json @@ -9,6 +9,9 @@ "lib", "dist" ], + "scripts": { + "test:typescript": "exit 0" + }, "dependencies": { "@babel/helper-module-imports": "^7.0.0", "@emotion/hash": "0.7.3", diff --git a/packages/babel-plugin-jsx-pragmatic/package.json b/packages/babel-plugin-jsx-pragmatic/package.json index 3bb682079..987feae86 100644 --- a/packages/babel-plugin-jsx-pragmatic/package.json +++ b/packages/babel-plugin-jsx-pragmatic/package.json @@ -6,6 +6,9 @@ "module": "dist/babel-plugin-jsx-pragmatic.esm.js", "license": "MIT", "repository": "https://github.com/emotion-js/emotion/tree/master/packages/babel-plugin-jsx-pragmatic", + "scripts": { + "test:typescript": "exit 0" + }, "dependencies": { "@babel/plugin-syntax-jsx": "^7.2.0" }, diff --git a/packages/babel-preset-css-prop/package.json b/packages/babel-preset-css-prop/package.json index 18e1a8903..985b85568 100644 --- a/packages/babel-preset-css-prop/package.json +++ b/packages/babel-preset-css-prop/package.json @@ -6,6 +6,9 @@ "module": "dist/babel-preset-css-prop.esm.js", "license": "MIT", "repository": "https://github.com/emotion-js/emotion/tree/master/packages/babel-preset-css-prop", + "scripts": { + "test:typescript": "exit 0" + }, "dependencies": { "@babel/plugin-transform-react-jsx": "^7.3.0", "@babel/runtime": "^7.5.5", diff --git a/packages/core/types/index.d.ts b/packages/core/types/index.d.ts index 9395e0133..a6149acbb 100644 --- a/packages/core/types/index.d.ts +++ b/packages/core/types/index.d.ts @@ -9,7 +9,7 @@ import { ComponentClass, Context, Provider, - SFC, + FC, ReactElement, ReactNode, Ref, @@ -30,7 +30,7 @@ export const ThemeContext: Context export const CacheProvider: Provider export function withEmotionCache( func: (props: Props, context: EmotionCache, ref: Ref) => ReactNode -): SFC> +): FC> export const jsx: typeof createElement @@ -45,7 +45,9 @@ export interface GlobalProps { * @desc * JSX generic are supported only after TS@2.9 */ -export function Global(props: GlobalProps): ReactElement +export function Global( + props: GlobalProps +): ReactElement export function keyframes( template: TemplateStringsArray, @@ -75,7 +77,7 @@ export interface ClassNamesProps { * @desc * JSX generic are supported only after TS@2.9 */ -export function ClassNames( +export function ClassNames( props: ClassNamesProps ): ReactElement diff --git a/packages/core/types/tests.tsx b/packages/core/types/tests.tsx index d880d41e6..fc9a6a565 100644 --- a/packages/core/types/tests.tsx +++ b/packages/core/types/tests.tsx @@ -4,8 +4,6 @@ import { ClassNames, ClassNamesContent, Global, - Interpolation, - CacheProvider, css, jsx, keyframes, diff --git a/packages/create-emotion/types/tests.ts b/packages/create-emotion/types/tests.ts index 4023bb04b..a6002a171 100644 --- a/packages/create-emotion/types/tests.ts +++ b/packages/create-emotion/types/tests.ts @@ -4,7 +4,7 @@ import createEmotion from 'create-emotion' const emotion0 = createEmotion() // $ExpectType Emotion const emotion1 = createEmotion({ - container: document.head !== null ? document.head : undefined, + container: document.head, nonce: 'fasefw' }) diff --git a/packages/create-emotion/types/tslint.json b/packages/create-emotion/types/tslint.json index 5bf1b9014..bf69b48bd 100644 --- a/packages/create-emotion/types/tslint.json +++ b/packages/create-emotion/types/tslint.json @@ -15,6 +15,7 @@ "check-typecast", "check-type-operator", "check-preblock" - ] + ], + "no-null-undefined-union": false } -} +} \ No newline at end of file diff --git a/packages/css/types/index.d.ts b/packages/css/types/index.d.ts index ca4d02766..b965c0beb 100644 --- a/packages/css/types/index.d.ts +++ b/packages/css/types/index.d.ts @@ -1,6 +1,10 @@ // Definitions by: Junyoung Clare Jang // TypeScript Version: 2.8 -import { Interpolation, SerializedStyles } from '@emotion/serialize' +import { + Interpolation, + SerializedStyles, + CSSInterpolation +} from '@emotion/serialize' export { ArrayInterpolation, @@ -14,6 +18,6 @@ export { Interpolation, SerializedStyles } export default function css( template: TemplateStringsArray, - ...args: Array + ...args: Array ): SerializedStyles -export default function css(...args: Array): SerializedStyles +export default function css(...args: Array): SerializedStyles diff --git a/packages/css/types/tests.ts b/packages/css/types/tests.ts index 0db207267..c8927b5be 100644 --- a/packages/css/types/tests.ts +++ b/packages/css/types/tests.ts @@ -31,6 +31,7 @@ css` // $ExpectError css(() => 'height: 300px;') + // $ExpectError css` position: relative; diff --git a/packages/emotion-theming/types/helper.d.ts b/packages/emotion-theming/types/helper.d.ts deleted file mode 100644 index faf29011d..000000000 --- a/packages/emotion-theming/types/helper.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import * as React from 'react' - -export type PropsOf< - C extends keyof JSX.IntrinsicElements | React.JSXElementConstructor -> = JSX.LibraryManagedAttributes> - -export type Omit = T extends any ? Pick> : never -export type AddOptionalTo = Omit & - Partial>> diff --git a/packages/emotion-theming/types/index.d.ts b/packages/emotion-theming/types/index.d.ts index 4080d83c9..32f0a4632 100644 --- a/packages/emotion-theming/types/index.d.ts +++ b/packages/emotion-theming/types/index.d.ts @@ -3,30 +3,43 @@ import * as React from 'react' -import { AddOptionalTo, PropsOf } from './helper' +import { + StyledComponent, + StyledOptions, + CreateStyledComponent, + StyledTags +} from '@emotion/styled' +import { PropsOf, DistributiveOmit } from '@emotion/styled-base' export interface ThemeProviderProps { theme: Partial | ((outerTheme: Theme) => Theme) children?: React.ReactNode } -export function ThemeProvider( - props: ThemeProviderProps -): React.ReactElement - -export function useTheme(): Theme +export interface ThemeProvider { + (props: ThemeProviderProps): React.ReactElement +} -/** - * @todo Add more constraint to C so that - * this function only accepts components with theme props. - */ -export function withTheme>( +export type withTheme = < + C extends React.ComponentType> +>( component: C -): React.SFC, 'theme'>> +) => React.FC, 'theme'> & { theme?: Theme }> + +export type useTheme = () => T + +export const ThemeProvider: ThemeProvider + +export const withTheme: withTheme + +export const useTheme: useTheme export interface EmotionTheming { - ThemeProvider(props: ThemeProviderProps): React.ReactElement - withTheme>( - component: C - ): React.SFC, 'theme'>> + ThemeProvider: ThemeProvider + withTheme: withTheme + useTheme: useTheme } + +export type WithTheme = P extends { theme: infer Theme } + ? P & { theme: Exclude } + : P & { theme: T } diff --git a/packages/emotion-theming/types/tests.tsx b/packages/emotion-theming/types/tests.tsx index d62277d72..70f212304 100644 --- a/packages/emotion-theming/types/tests.tsx +++ b/packages/emotion-theming/types/tests.tsx @@ -1,5 +1,7 @@ import * as emotionTheming from 'emotion-theming' import * as React from 'react' +import styled, { CreateStyled } from '@emotion/styled' +import { Interpolation, ObjectInterpolation } from '@emotion/styled-base' const { ThemeProvider, withTheme, useTheme } = emotionTheming @@ -11,26 +13,25 @@ declare const theme: Theme interface Props { prop: boolean - theme: Theme } -declare const CompSFC: React.SFC -declare class CompC extends React.Component {} +declare const CompFC: React.FC +declare class CompC extends React.Component {} -const WrappedCompC = withTheme(CompC) +const WrappedCompC = withTheme(CompC) ;{WrappedCompC} ; theme} /> ; ({ ...outerTheme, ...theme })} /> -const ThemedSFC = withTheme(CompSFC) -; -; +const ThemedFC = withTheme(CompFC) +; +; const ThemedComp = withTheme(CompC) ; ; -const CompSFCWithDefault = ({ prop }: Props) => (prop ? :
) -CompSFCWithDefault.defaultProps = { prop: false } +const CompFCWithDefault = ({ prop }: Props) => (prop ? :
) +CompFCWithDefault.defaultProps = { prop: false } class CompCWithDefault extends React.Component { static defaultProps = { prop: false } render() { @@ -39,13 +40,14 @@ class CompCWithDefault extends React.Component { } { - const theme: Theme = useTheme() + const theme: Theme = useTheme() + const themeFail: Theme = useTheme() // $ExpectError } -const ThemedSFCWithDefault = withTheme(CompSFCWithDefault) -; -; +const ThemedFCWithDefault = withTheme(CompFCWithDefault) +; +; const ThemedCompWithDefault = withTheme(CompCWithDefault) ; @@ -59,7 +61,8 @@ const { // $ExpectError ; -typedWithTheme(CompSFC) +typedWithTheme(CompFC) + /** * @todo * Following line should report an error. @@ -79,7 +82,7 @@ typedWithTheme((props: { value: number }) => null) type SomethingToRead = (Book | Magazine) & { theme?: any } - const Readable: React.SFC = props => { + const Readable: React.FC = props => { if (props.kind === 'magazine') { return
magazine #{props.issue}
} @@ -93,3 +96,44 @@ typedWithTheme((props: { value: number }) => null) ; // $ExpectError ; // $ExpectError } + +const themedStyled = styled as CreateStyled + +const StyledCompC = themedStyled(WrappedCompC)({}) +const AdditionallyStyledCompC = themedStyled(StyledCompC)({}) +; +; + +const StyledDiv = themedStyled('div')({}) +; +// $ExpectError +; +const AdditionallyStyledDiv = themedStyled(StyledDiv)({}) +; +// $ExpectError +; + +const StyledDiv2 = themedStyled.div({}) +; +// $ExpectError +; + +export type StyleDefinition = Interpolation< + emotionTheming.WithTheme +> +export type ObjectStyleDefinition = ObjectInterpolation< + emotionTheming.WithTheme +> + +const style: StyleDefinition = ({ theme }) => ({ + color: theme.primary +}) +const style2: ObjectStyleDefinition = { + width: 100 +} + +// Can use ThemeProvider +; +; +// $ExpectError +; diff --git a/packages/emotion-theming/types/tslint.json b/packages/emotion-theming/types/tslint.json index e094153f1..a7a8dedbe 100644 --- a/packages/emotion-theming/types/tslint.json +++ b/packages/emotion-theming/types/tslint.json @@ -1,21 +1,22 @@ { - "extends": "dtslint/dtslint.json", - "rules": { - "array-type": [true, "generic"], - "semicolon": false, - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-module", - "check-rest-spread", - "check-type", - "check-typecast", - "check-type-operator", - "check-preblock" - ], - "no-null-undefined-union": false, - "no-unnecessary-generics": false - } + "extends": "dtslint/dtslint.json", + "rules": { + "array-type": [true, "generic"], + "semicolon": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-module", + "check-rest-spread", + "check-type", + "check-typecast", + "check-type-operator", + "check-preblock" + ], + "no-null-undefined-union": false, + "no-unnecessary-generics": false, + "callable-types": false + } } diff --git a/packages/eslint-plugin-emotion/package.json b/packages/eslint-plugin-emotion/package.json index ca1407349..51357d3a3 100644 --- a/packages/eslint-plugin-emotion/package.json +++ b/packages/eslint-plugin-emotion/package.json @@ -2,6 +2,9 @@ "name": "eslint-plugin-emotion", "version": "10.0.14", "description": "ESLint rules for emotion", + "scripts": { + "test:typescript": "exit 0" + }, "keywords": [ "eslint", "eslintplugin", diff --git a/packages/jest-emotion/types/tsconfig.json b/packages/jest-emotion/types/tsconfig.json index ea6734342..e60ab31a3 100644 --- a/packages/jest-emotion/types/tsconfig.json +++ b/packages/jest-emotion/types/tsconfig.json @@ -15,9 +15,6 @@ "strictNullChecks": true, "strictFunctionTypes": true, "target": "es5", - "typeRoots": [ - "../" - ], "types": [] }, "include": [ diff --git a/packages/memoize/types/tslint.json b/packages/memoize/types/tslint.json index b9706ef70..5da4cfaab 100644 --- a/packages/memoize/types/tslint.json +++ b/packages/memoize/types/tslint.json @@ -20,6 +20,7 @@ "check-preblock" ], - "no-unnecessary-generics": false + "no-unnecessary-generics": false, + "strict-export-declare-modifiers": false } -} +} \ No newline at end of file diff --git a/packages/native/package.json b/packages/native/package.json index 809222def..4d67e613e 100644 --- a/packages/native/package.json +++ b/packages/native/package.json @@ -4,6 +4,9 @@ "description": "Style and render React Native components using emotion", "main": "dist/native.cjs.js", "module": "dist/native.esm.js", + "scripts": { + "test:typescript": "exit 0" + }, "files": [ "src", "dist" diff --git a/packages/primitives-core/package.json b/packages/primitives-core/package.json index b77515232..5fe72b713 100644 --- a/packages/primitives-core/package.json +++ b/packages/primitives-core/package.json @@ -4,6 +4,9 @@ "description": "Shared utilities for emotion primitives and native", "main": "dist/primitives-core.cjs.js", "module": "dist/primitives-core.esm.js", + "scripts": { + "test:typescript": "exit 0" + }, "files": [ "src", "dist" diff --git a/packages/primitives/package.json b/packages/primitives/package.json index 2ade17872..18d30f4b7 100644 --- a/packages/primitives/package.json +++ b/packages/primitives/package.json @@ -3,6 +3,9 @@ "version": "11.0.0-next.0", "main": "dist/primitives.cjs.js", "module": "dist/primitives.esm.js", + "scripts": { + "test:typescript": "exit 0" + }, "files": [ "src", "dist" diff --git a/packages/serialize/types/helper.d.ts b/packages/serialize/types/helper.d.ts deleted file mode 100644 index ef9c8246a..000000000 --- a/packages/serialize/types/helper.d.ts +++ /dev/null @@ -1 +0,0 @@ -export type Equal = A extends B ? (B extends A ? T : F) : F diff --git a/packages/serialize/types/index.d.ts b/packages/serialize/types/index.d.ts index e4f58419f..606bb7545 100644 --- a/packages/serialize/types/index.d.ts +++ b/packages/serialize/types/index.d.ts @@ -4,8 +4,6 @@ import { RegisteredCache, SerializedStyles } from '@emotion/utils' import * as CSS from 'csstype' -import { Equal } from './helper' - export { RegisteredCache, SerializedStyles } export type CSSProperties = CSS.PropertiesFallback @@ -63,9 +61,12 @@ export interface ObjectInterpolation extends CSSPropertiesWithMultiValues, CSSPseudos, CSSOthersObject {} -export type FunctionInterpolation = (mergedProps: MP) => Interpolation -export type Interpolation = +export interface FunctionInterpolation { + (mergedProps: MergedProps): Interpolation +} + +export type Interpolation = | null | undefined | boolean @@ -74,9 +75,9 @@ export type Interpolation = | ComponentSelector | Keyframes | SerializedStyles - | ArrayInterpolation - | ObjectInterpolation - | Equal> + | ArrayInterpolation + | ObjectInterpolation + | FunctionInterpolation export function serializeStyles( args: Array>, diff --git a/packages/serialize/types/tests.ts b/packages/serialize/types/tests.ts index 729d5d026..bb4df5872 100644 --- a/packages/serialize/types/tests.ts +++ b/packages/serialize/types/tests.ts @@ -16,25 +16,21 @@ const testObjectInterpolation1: ObjectInterpolation = { } // $ExpectType SerializedStyles -serializeStyles({}, []) +serializeStyles([], {}) // $ExpectType SerializedStyles -serializeStyles( - { - 'emotion-cache': 'width: 200px' - }, - [] -) +serializeStyles([], { + 'emotion-cache': 'width: 200px' +}) // $ExpectType SerializedStyles -serializeStyles({}, [], {}) +serializeStyles([], {}, {}) // $ExpectType SerializedStyles -serializeStyles({}, ['abc'], {}) +serializeStyles(['abc'], {}, {}) // $ExpectType SerializedStyles -serializeStyles({}, ['width: 200px;'], {}) +serializeStyles(['width: 200px;'], {}, {}) // $ExpectType SerializedStyles -serializeStyles({}, [() => 'height: 300px;'], {}) +serializeStyles([() => 'height: 300px;'], {}, {}) // $ExpectType SerializedStyles serializeStyles( - {}, [ 'display: block;', { @@ -42,10 +38,11 @@ serializeStyles( backgroundColor: 'red' } ], + {}, {} ) // $ExpectType SerializedStyles -serializeStyles({}, [testTemplateStringsArray, 5, '4px'], {}) +serializeStyles([testTemplateStringsArray, 5, '4px'], {}, {}) // $ExpectError serializeStyles() diff --git a/packages/serialize/types/tslint.json b/packages/serialize/types/tslint.json index eec7af5ea..4ffac3c17 100644 --- a/packages/serialize/types/tslint.json +++ b/packages/serialize/types/tslint.json @@ -5,6 +5,8 @@ true, "generic" ], - "semicolon": false + "semicolon": false, + "no-null-undefined-union": false, + "callable-types": false } -} +} \ No newline at end of file diff --git a/packages/sheet/types/tests.ts b/packages/sheet/types/tests.ts index cd9066cd0..de636a96f 100644 --- a/packages/sheet/types/tests.ts +++ b/packages/sheet/types/tests.ts @@ -1,4 +1,4 @@ -import { Options, StyleSheet } from '@emotion/sheet' +import { StyleSheet } from '@emotion/sheet' new StyleSheet({ key: 'abc', @@ -44,11 +44,6 @@ styleSheet.insert('.cl{ width: 200px; height: 200px; }') styleSheet.insert() // $ExpectError styleSheet.insert('.name{ color: black; }', undefined as any) -// $ExpectError -styleSheet.insert( - '.name{ color: black; }', - ...((undefined as any) as Array) -) styleSheet.flush() // $ExpectError diff --git a/packages/styled-base/types/helper.d.ts b/packages/styled-base/types/helper.d.ts index 50bd0e77c..040c40c1d 100644 --- a/packages/styled-base/types/helper.d.ts +++ b/packages/styled-base/types/helper.d.ts @@ -5,7 +5,9 @@ import * as React from 'react' */ export type PropsOf< C extends keyof JSX.IntrinsicElements | React.JSXElementConstructor -> = JSX.LibraryManagedAttributes> +> = JSX.LibraryManagedAttributes> -export type Omit = T extends any ? Pick> : never -export type Overwrapped = Pick> +// We need to use this version of Omit as it's distributive (Will preserve unions) +export type DistributiveOmit = T extends any + ? Pick> + : never diff --git a/packages/styled-base/types/index.d.ts b/packages/styled-base/types/index.d.ts index 52dfe2fb3..240394bb5 100644 --- a/packages/styled-base/types/index.d.ts +++ b/packages/styled-base/types/index.d.ts @@ -1,5 +1,5 @@ // Definitions by: Junyoung Clare Jang -// TypeScript Version: 3.1 +// TypeScript Version: 3.2 /** * @desc @@ -12,10 +12,9 @@ * a style of that component. */ -import { ComponentSelector, Interpolation } from '@emotion/serialize' import * as React from 'react' - -import { Omit, Overwrapped, PropsOf } from './helper' +import { ComponentSelector, Interpolation } from '@emotion/serialize' +import { PropsOf, DistributiveOmit } from './helper' export { ArrayInterpolation, @@ -24,13 +23,7 @@ export { ObjectInterpolation } from '@emotion/serialize' -export { ComponentSelector, Interpolation } - -type JSXInEl = JSX.IntrinsicElements - -export type WithTheme = P extends { theme: infer Theme } - ? P & { theme: Exclude } - : P & { theme: T } +export { ComponentSelector, Interpolation, PropsOf, DistributiveOmit } export interface StyledOptions { label?: string @@ -38,113 +31,76 @@ export interface StyledOptions { target?: string } -export interface StyledComponent - extends React.SFC, - ComponentSelector { - /** - * @desc this method is type-unsafe - */ - withComponent( - tag: NewTag - ): StyledComponent - withComponent>( +/** + * @typeparam ComponentProps Props which will be included when withComponent is called + * @typeparam SpecificComponentProps Props which will *not* be included when withComponent is called + */ +export interface StyledComponent< + ComponentProps extends {}, + SpecificComponentProps extends {} = {} +> extends React.FC, ComponentSelector { + withComponent>>( + component: C + ): StyledComponent> + withComponent( tag: Tag - ): StyledComponent, StyleProps, Theme> -} - -type ReactClassPropKeys = keyof React.ClassAttributes - -interface CreateStyledComponentBaseThemeless { - < - StyleProps extends Omit< - Overwrapped, - ReactClassPropKeys - > = Omit, - Theme extends object = object - >( - ...styles: Array>> - ): StyledComponent - < - StyleProps extends Omit< - Overwrapped, - ReactClassPropKeys - > = Omit, - Theme extends object = object - >( - template: TemplateStringsArray, - ...styles: Array>> - ): StyledComponent + ): StyledComponent } -interface CreateStyledComponentBaseThemed< - InnerProps, - ExtraProps, - StyledInstanceTheme extends object +/** + * @typeparam ComponentProps Props which will be included when withComponent is called + * @typeparam SpecificComponentProps Props which will *not* be included when withComponent is called + * @typeparam StyleProps Params passed to styles but not exposed as React props. These are normally library provided props + */ +export interface CreateStyledComponent< + ComponentProps extends {}, + SpecificComponentProps extends {} = {}, + StyleProps extends {} = {} > { - < - StyleProps extends Omit< - Overwrapped, - ReactClassPropKeys - > = Omit - >( - ...styles: Array>> - ): StyledComponent - < - StyleProps extends Omit< - Overwrapped, - ReactClassPropKeys - > = Omit - >( + /** + * @typeparam AdditionalProps Additional props to add to your styled component + */ + ( + ...styles: Array< + Interpolation< + ComponentProps & SpecificComponentProps & StyleProps & AdditionalProps + > + > + ): StyledComponent + /** + * @typeparam AdditionalProps Additional props to add to your styled component + */ + ( template: TemplateStringsArray, - ...styles: Array>> - ): StyledComponent + ...styles: Array< + Interpolation + > + ): StyledComponent } -export type CreateStyledComponentBase< - InnerProps, - ExtraProps, - StyledInstanceTheme extends object -> = - // this "reversed" condition checks if StyledInstanceTheme was already parametrized when using CreateStyled - object extends StyledInstanceTheme - ? CreateStyledComponentBaseThemeless - : CreateStyledComponentBaseThemed< - InnerProps, - ExtraProps, - StyledInstanceTheme - > - -export type CreateStyledComponentIntrinsic< - Tag extends keyof JSXInEl, - ExtraProps, - Theme extends object -> = CreateStyledComponentBase -export type CreateStyledComponentExtrinsic< - Tag extends React.ComponentType, - ExtraProps, - Theme extends object -> = CreateStyledComponentBase, ExtraProps, Theme> - /** * @desc - * This function accepts `InnerProps`/`Tag` to infer the type of `tag`, - * and accepts `ExtraProps` for user who use string style - * to be able to declare extra props without using - * `` styled('button')`...` ``, which does not supported in - * styled-component VSCode extension. - * If your tool support syntax highlighting for `` styled('button')`...` `` - * it could be more efficient. + * This function accepts a React component or tag ('div', 'a' etc). + * + * @example styled(MyComponent)({ width: 100 }) + * @example styled(MyComponent)(myComponentProps => ({ width: myComponentProps.width }) + * @example styled('div')({ width: 100 }) + * @example styled('div')(props => ({ width: props.width }) */ -export interface CreateStyled { - , ExtraProps = {}>( - tag: Tag, +export interface CreateStyled { + >>( + component: C, options?: StyledOptions - ): CreateStyledComponentExtrinsic + ): CreateStyledComponent & { theme?: Theme }, {}, { theme: Theme }> - ( + ( tag: Tag, options?: StyledOptions - ): CreateStyledComponentIntrinsic + ): CreateStyledComponent< + { theme?: Theme }, + JSX.IntrinsicElements[Tag], + { theme: Theme } + > } declare const styled: CreateStyled diff --git a/packages/styled-base/types/tests.tsx b/packages/styled-base/types/tests.tsx index 864d4050c..ace46c4af 100644 --- a/packages/styled-base/types/tests.tsx +++ b/packages/styled-base/types/tests.tsx @@ -5,7 +5,7 @@ import styled from '@emotion/styled-base' type ReactClassProps0 = { readonly column: boolean } -declare class ReactClassComponent0 extends React.Component {} +class ReactClassComponent0 extends React.Component {} interface ReactClassProps1 { readonly value: string @@ -18,20 +18,20 @@ interface ReactClassProps2 { declare class ReactClassComponent2 extends React.Component {} // tslint:disable-next-line: interface-over-type-literal -type ReactSFCProps0 = { +type ReactFCProps0 = { readonly column: boolean } -declare const ReactSFC0: React.SFC +declare const ReactFC0: React.FC -interface ReactSFCProps1 { +interface ReactFCProps1 { readonly value: string } -declare const ReactSFC1: React.SFC +declare const ReactFC1: React.FC -interface ReactSFCProps2 { +interface ReactFCProps2 { readonly value: number } -declare const ReactSFC2: React.SFC +declare const ReactFC2: React.FC const Button0 = styled('button')` color: blue; @@ -62,6 +62,23 @@ const Input1 = styled('input', { ; const Input2 = Button0.withComponent('input') +const View = styled('div')({ + display: 'flex', + position: 'relative', + boxSizing: 'border-box', + flexDirection: 'column' +}) + +const Input3 = styled(View.withComponent('input'))({ + color: 'red' +}) +; { + // $ExpectType ChangeEvent + e + }} +/> + const Canvas0 = styled('canvas', { shouldForwardProp(propName) { return propName === 'width' || propName === 'height' @@ -82,22 +99,15 @@ const Canvas1 = styled('canvas', { interface PrimaryProps { readonly primary: string } -/** - * @desc - * This function accepts `InnerProps`/`Tag` to infer the type of `tag`, - * and accepts `ExtraProps` for user who use string style - * to be able to declare extra props without using - * `` styled('button')`...` ``, which does not supported in - * styled-component VSCode extension. - * If your tool support syntax highlighting for `` styled('button')`...` `` - * it could be more efficient. - */ -const Button2 = styled<'button', PrimaryProps>('button')` - fontsize: ${5}px; + +const Button2 = styled('button')` + font-size: ${5}px; color: ${props => props.primary}; ` -const Button3 = styled<'button', PrimaryProps>('button')(props => ({ - color: props.primary +const Button3 = styled('button')(props => ({ + color: props.primary, + // Verify we get access to intrinsic props + display: props.hidden ? 'none' : undefined })) ;
@@ -116,15 +126,13 @@ const Button3 = styled<'button', PrimaryProps>('button')(props => ({ // $ExpectError ; -const Button4 = styled( - ReactClassComponent0 -)` - backgroundColor: ${props => props.theme.backColor} +const Button4 = styled(ReactClassComponent0)` + background-color: ${props => props.theme.backColor}; - fontSize: ${5}px; - color: ${props => props.primary} + font-size: ${5}px; + color: ${props => props.primary}; ` -const Button5 = styled(ReactSFC0)(props => ({ +const Button5 = styled(ReactFC0)(props => ({ color: props.primary })) ;
@@ -146,19 +154,24 @@ const Button5 = styled(ReactSFC0)(props => ({ const Container0 = styled(ReactClassComponent0)` display: flex; - flexdirection: ${props => props.column && 'column'}; + flex-direction: ${props => props.column && 'column'}; ` ; // $ExpectError ; +// When we change component, the original props still need to be available +// as the original styles may be using those props const Container1 = Container0.withComponent('span') ; ; + +// $ExpectError +; // $ExpectError ; -const Container2 = Container0.withComponent(ReactSFC0) +const Container2 = Container0.withComponent(ReactFC0) ; // $ExpectError ; @@ -173,7 +186,7 @@ const Container3 = Container0.withComponent(ReactClassComponent1) interface ContainerProps { extraWidth: string } -const Container4 = styled(ReactSFC2)(props => ({ +const Container4 = styled(ReactFC2)(props => ({ borderColor: 'black', borderWidth: props.extraWidth, borderStyle: 'solid' @@ -184,7 +197,8 @@ const Container4 = styled(ReactSFC2)(props => ({ // $ExpectError ; -const Container5 = Container3.withComponent(ReactSFC2) +const Container5 = Container3.withComponent(ReactFC2) +// $ExpectError ; // $ExpectError ; @@ -193,20 +207,35 @@ const Container5 = Container3.withComponent(ReactSFC2) // $ExpectError ; -// $ExpectError -styled(ReactSFC2)() - /** * @todo * I wish we could raise errors for following two `withComponent`s. + * The prduces a component type which is invalid (intersection of number & string is never), but you need to consume the component + * to see the error. */ -Container0.withComponent(ReactClassComponent2) -Container3.withComponent(ReactClassComponent2) +const C02 = Container0.withComponent(ReactClassComponent2) +// $ExpectError +; +const C03 = Container3.withComponent(ReactClassComponent2) +// $ExpectError +; + +const ForwardRefCheckStyled = styled( + React.forwardRef( + (props: ReactClassProps0, ref: React.Ref) => { + return
+ } + ) +)({}) + +// Expose ref only when inner component is forwarding refs +;()} /> const StyledClass0 = styled(ReactClassComponent0)({}) declare const ref0_0: (element: ReactClassComponent0 | null) => void declare const ref0_1: (element: ReactClassComponent1 | null) => void declare const ref0_2: (element: HTMLDivElement | null) => void +// $ExpectError ; // $ExpectError ; @@ -217,6 +246,7 @@ const StyledClass1 = StyledClass0.withComponent(ReactClassComponent1) declare const ref1_0: (element: ReactClassComponent1 | null) => void declare const ref1_1: (element: ReactClassComponent0 | null) => void declare const ref1_2: (element: HTMLDivElement | null) => void +// $ExpectError ; // $ExpectError ; @@ -237,6 +267,7 @@ const StyledClass3 = StyledClass1.withComponent('label') declare const ref3_0: (element: HTMLLabelElement | null) => void declare const ref3_1: (element: ReactClassComponent0 | null) => void declare const ref3_2: (element: HTMLDivElement | null) => void +// $ExpectError ; // $ExpectError ; @@ -256,7 +287,7 @@ declare const ref3_2: (element: HTMLDivElement | null) => void type SomethingToRead = Book | Magazine - const Readable: React.SFC = props => { + const Readable: React.FC = props => { if (props.kind === 'magazine') { return
magazine #{props.issue}
} @@ -296,3 +327,73 @@ const StyledFCWithDefaultProps = styled(FCWithDefaultProps)` background-color: red; ` const fcInstance = + +interface PropsA { + title: string +} +interface PropsB { + content: string +} + +type PropsAB = PropsA | PropsB +class A extends React.Component { + static getDerivedStateFromProps(props: PropsAB) { + return null + } + + render() { + return null + } +} + +const B = styled(A)` + color: red; +` +; +; +// Because these are not tagged unions, they are not mutually exclusive, we can do both +; + +interface TaggedPropsA { + tag: 'a' + title: string +} +interface TaggedPropsB { + tag: 'b' + content: string +} +type TaggedPropsAB = TaggedPropsA | TaggedPropsB +class C extends React.Component { + render() { + return null + } +} + +const D = styled(C)` + color: red; +` +; +; +// $ExpectError +; + +// Ensure refs are available on intrinsic components and they are correct types +const StyledDiv = styled('div')({}) +declare const ref4_0: (element: ReactClassComponent1 | null) => void +declare const ref4_1: (element: ReactClassComponent0 | null) => void +declare const ref4_2: (element: HTMLDivElement | null) => void +// $ExpectError +; +// $ExpectError +; +; +; + +// test it should drop intrinsic props from first component when withComponent is called +const StyledButton = StyledDiv.withComponent('button') +; { + // $ExpectType MouseEvent + e + }} +/> diff --git a/packages/styled/types/index.d.ts b/packages/styled/types/index.d.ts index 14347014f..02d171ea7 100644 --- a/packages/styled/types/index.d.ts +++ b/packages/styled/types/index.d.ts @@ -1,177 +1,32 @@ // Definitions by: Junyoung Clare Jang -// TypeScript Version: 2.8 +// TypeScript Version: 2.9 import { CreateStyled as BaseCreateStyled, - CreateStyledComponentIntrinsic + CreateStyledComponent } from '@emotion/styled-base' export { ArrayInterpolation, ComponentSelector, - CreateStyledComponentBase, - CreateStyledComponentExtrinsic, - CreateStyledComponentIntrinsic, CSSObject, FunctionInterpolation, Interpolation, ObjectInterpolation, StyledComponent, StyledOptions, - WithTheme + CreateStyledComponent } from '@emotion/styled-base' -export interface StyledTags { - /** - * @desc - * HTML tags - */ - a: CreateStyledComponentIntrinsic<'a', {}, Theme> - abbr: CreateStyledComponentIntrinsic<'abbr', {}, Theme> - address: CreateStyledComponentIntrinsic<'address', {}, Theme> - area: CreateStyledComponentIntrinsic<'area', {}, Theme> - article: CreateStyledComponentIntrinsic<'article', {}, Theme> - aside: CreateStyledComponentIntrinsic<'aside', {}, Theme> - audio: CreateStyledComponentIntrinsic<'audio', {}, Theme> - b: CreateStyledComponentIntrinsic<'b', {}, Theme> - base: CreateStyledComponentIntrinsic<'base', {}, Theme> - bdi: CreateStyledComponentIntrinsic<'bdi', {}, Theme> - bdo: CreateStyledComponentIntrinsic<'bdo', {}, Theme> - big: CreateStyledComponentIntrinsic<'big', {}, Theme> - blockquote: CreateStyledComponentIntrinsic<'blockquote', {}, Theme> - body: CreateStyledComponentIntrinsic<'body', {}, Theme> - br: CreateStyledComponentIntrinsic<'br', {}, Theme> - button: CreateStyledComponentIntrinsic<'button', {}, Theme> - canvas: CreateStyledComponentIntrinsic<'canvas', {}, Theme> - caption: CreateStyledComponentIntrinsic<'caption', {}, Theme> - cite: CreateStyledComponentIntrinsic<'cite', {}, Theme> - code: CreateStyledComponentIntrinsic<'code', {}, Theme> - col: CreateStyledComponentIntrinsic<'col', {}, Theme> - colgroup: CreateStyledComponentIntrinsic<'colgroup', {}, Theme> - data: CreateStyledComponentIntrinsic<'data', {}, Theme> - datalist: CreateStyledComponentIntrinsic<'datalist', {}, Theme> - dd: CreateStyledComponentIntrinsic<'dd', {}, Theme> - del: CreateStyledComponentIntrinsic<'del', {}, Theme> - details: CreateStyledComponentIntrinsic<'details', {}, Theme> - dfn: CreateStyledComponentIntrinsic<'dfn', {}, Theme> - dialog: CreateStyledComponentIntrinsic<'dialog', {}, Theme> - div: CreateStyledComponentIntrinsic<'div', {}, Theme> - dl: CreateStyledComponentIntrinsic<'dl', {}, Theme> - dt: CreateStyledComponentIntrinsic<'dt', {}, Theme> - em: CreateStyledComponentIntrinsic<'em', {}, Theme> - embed: CreateStyledComponentIntrinsic<'embed', {}, Theme> - fieldset: CreateStyledComponentIntrinsic<'fieldset', {}, Theme> - figcaption: CreateStyledComponentIntrinsic<'figcaption', {}, Theme> - figure: CreateStyledComponentIntrinsic<'figure', {}, Theme> - footer: CreateStyledComponentIntrinsic<'footer', {}, Theme> - form: CreateStyledComponentIntrinsic<'form', {}, Theme> - h1: CreateStyledComponentIntrinsic<'h1', {}, Theme> - h2: CreateStyledComponentIntrinsic<'h2', {}, Theme> - h3: CreateStyledComponentIntrinsic<'h3', {}, Theme> - h4: CreateStyledComponentIntrinsic<'h4', {}, Theme> - h5: CreateStyledComponentIntrinsic<'h5', {}, Theme> - h6: CreateStyledComponentIntrinsic<'h6', {}, Theme> - head: CreateStyledComponentIntrinsic<'head', {}, Theme> - header: CreateStyledComponentIntrinsic<'header', {}, Theme> - hgroup: CreateStyledComponentIntrinsic<'hgroup', {}, Theme> - hr: CreateStyledComponentIntrinsic<'hr', {}, Theme> - html: CreateStyledComponentIntrinsic<'html', {}, Theme> - i: CreateStyledComponentIntrinsic<'i', {}, Theme> - iframe: CreateStyledComponentIntrinsic<'iframe', {}, Theme> - img: CreateStyledComponentIntrinsic<'img', {}, Theme> - input: CreateStyledComponentIntrinsic<'input', {}, Theme> - ins: CreateStyledComponentIntrinsic<'ins', {}, Theme> - kbd: CreateStyledComponentIntrinsic<'kbd', {}, Theme> - keygen: CreateStyledComponentIntrinsic<'keygen', {}, Theme> - label: CreateStyledComponentIntrinsic<'label', {}, Theme> - legend: CreateStyledComponentIntrinsic<'legend', {}, Theme> - li: CreateStyledComponentIntrinsic<'li', {}, Theme> - link: CreateStyledComponentIntrinsic<'link', {}, Theme> - main: CreateStyledComponentIntrinsic<'main', {}, Theme> - map: CreateStyledComponentIntrinsic<'map', {}, Theme> - mark: CreateStyledComponentIntrinsic<'mark', {}, Theme> - /** - * @desc - * marquee tag is not supported by @types/react - */ - // 'marquee': CreateStyledComponentIntrinsic<'marquee', {}, Theme>; - menu: CreateStyledComponentIntrinsic<'menu', {}, Theme> - menuitem: CreateStyledComponentIntrinsic<'menuitem', {}, Theme> - meta: CreateStyledComponentIntrinsic<'meta', {}, Theme> - meter: CreateStyledComponentIntrinsic<'meter', {}, Theme> - nav: CreateStyledComponentIntrinsic<'nav', {}, Theme> - noscript: CreateStyledComponentIntrinsic<'noscript', {}, Theme> - object: CreateStyledComponentIntrinsic<'object', {}, Theme> - ol: CreateStyledComponentIntrinsic<'ol', {}, Theme> - optgroup: CreateStyledComponentIntrinsic<'optgroup', {}, Theme> - option: CreateStyledComponentIntrinsic<'option', {}, Theme> - output: CreateStyledComponentIntrinsic<'output', {}, Theme> - p: CreateStyledComponentIntrinsic<'p', {}, Theme> - param: CreateStyledComponentIntrinsic<'param', {}, Theme> - picture: CreateStyledComponentIntrinsic<'picture', {}, Theme> - pre: CreateStyledComponentIntrinsic<'pre', {}, Theme> - progress: CreateStyledComponentIntrinsic<'progress', {}, Theme> - q: CreateStyledComponentIntrinsic<'q', {}, Theme> - rp: CreateStyledComponentIntrinsic<'rp', {}, Theme> - rt: CreateStyledComponentIntrinsic<'rt', {}, Theme> - ruby: CreateStyledComponentIntrinsic<'ruby', {}, Theme> - s: CreateStyledComponentIntrinsic<'s', {}, Theme> - samp: CreateStyledComponentIntrinsic<'samp', {}, Theme> - script: CreateStyledComponentIntrinsic<'script', {}, Theme> - section: CreateStyledComponentIntrinsic<'section', {}, Theme> - select: CreateStyledComponentIntrinsic<'select', {}, Theme> - small: CreateStyledComponentIntrinsic<'small', {}, Theme> - source: CreateStyledComponentIntrinsic<'source', {}, Theme> - span: CreateStyledComponentIntrinsic<'span', {}, Theme> - strong: CreateStyledComponentIntrinsic<'strong', {}, Theme> - style: CreateStyledComponentIntrinsic<'style', {}, Theme> - sub: CreateStyledComponentIntrinsic<'sub', {}, Theme> - summary: CreateStyledComponentIntrinsic<'summary', {}, Theme> - sup: CreateStyledComponentIntrinsic<'sup', {}, Theme> - table: CreateStyledComponentIntrinsic<'table', {}, Theme> - tbody: CreateStyledComponentIntrinsic<'tbody', {}, Theme> - td: CreateStyledComponentIntrinsic<'td', {}, Theme> - textarea: CreateStyledComponentIntrinsic<'textarea', {}, Theme> - tfoot: CreateStyledComponentIntrinsic<'tfoot', {}, Theme> - th: CreateStyledComponentIntrinsic<'th', {}, Theme> - thead: CreateStyledComponentIntrinsic<'thead', {}, Theme> - time: CreateStyledComponentIntrinsic<'time', {}, Theme> - title: CreateStyledComponentIntrinsic<'title', {}, Theme> - tr: CreateStyledComponentIntrinsic<'tr', {}, Theme> - track: CreateStyledComponentIntrinsic<'track', {}, Theme> - u: CreateStyledComponentIntrinsic<'u', {}, Theme> - ul: CreateStyledComponentIntrinsic<'ul', {}, Theme> - var: CreateStyledComponentIntrinsic<'var', {}, Theme> - video: CreateStyledComponentIntrinsic<'video', {}, Theme> - wbr: CreateStyledComponentIntrinsic<'wbr', {}, Theme> - - /** - * @desc - * SVG tags - */ - circle: CreateStyledComponentIntrinsic<'circle', {}, Theme> - clipPath: CreateStyledComponentIntrinsic<'clipPath', {}, Theme> - defs: CreateStyledComponentIntrinsic<'defs', {}, Theme> - ellipse: CreateStyledComponentIntrinsic<'ellipse', {}, Theme> - foreignObject: CreateStyledComponentIntrinsic<'foreignObject', {}, Theme> - g: CreateStyledComponentIntrinsic<'g', {}, Theme> - image: CreateStyledComponentIntrinsic<'image', {}, Theme> - line: CreateStyledComponentIntrinsic<'line', {}, Theme> - linearGradient: CreateStyledComponentIntrinsic<'linearGradient', {}, Theme> - mask: CreateStyledComponentIntrinsic<'mask', {}, Theme> - path: CreateStyledComponentIntrinsic<'path', {}, Theme> - pattern: CreateStyledComponentIntrinsic<'pattern', {}, Theme> - polygon: CreateStyledComponentIntrinsic<'polygon', {}, Theme> - polyline: CreateStyledComponentIntrinsic<'polyline', {}, Theme> - radialGradient: CreateStyledComponentIntrinsic<'radialGradient', {}, Theme> - rect: CreateStyledComponentIntrinsic<'rect', {}, Theme> - stop: CreateStyledComponentIntrinsic<'stop', {}, Theme> - svg: CreateStyledComponentIntrinsic<'svg', {}, Theme> - text: CreateStyledComponentIntrinsic<'text', {}, Theme> - tspan: CreateStyledComponentIntrinsic<'tspan', {}, Theme> +export type StyledTags = { + [Tag in keyof JSX.IntrinsicElements]: CreateStyledComponent< + { theme?: Theme }, + JSX.IntrinsicElements[Tag], + { theme: Theme } + > } -export interface CreateStyled +export interface CreateStyled extends BaseCreateStyled, StyledTags {} diff --git a/packages/styled/types/tests.tsx b/packages/styled/types/tests.tsx index 8bb0ba594..bc7d5e4ad 100644 --- a/packages/styled/types/tests.tsx +++ b/packages/styled/types/tests.tsx @@ -1,35 +1,53 @@ +import * as React from 'react' import styled, { CreateStyled } from '@emotion/styled' -// $ExpectType CreateStyledComponentIntrinsic<"a", {}, any> +// $ExpectType CreateStyledComponent<{ theme?: any; }, DetailedHTMLProps, HTMLAnchorElement>, { theme: any; }> styled.a -// $ExpectType CreateStyledComponentIntrinsic<"body", {}, any> +// $ExpectType CreateStyledComponent<{ theme?: any; }, DetailedHTMLProps, HTMLBodyElement>, { theme: any; }> styled.body -// $ExpectType CreateStyledComponentIntrinsic<"div", {}, any> +// $ExpectType CreateStyledComponent<{ theme?: any; }, DetailedHTMLProps, HTMLDivElement>, { theme: any; }> styled.div -// $ExpectType CreateStyledComponentIntrinsic<"svg", {}, any> +// $ExpectType CreateStyledComponent<{ theme?: any; }, SVGProps, { theme: any; }> styled.svg { - // $ExpectType CreateStyledComponentIntrinsic<"svg", { bar: string }, { themed: "black" }> - styled.div<{ bar: string }, { themed: 'black' }>` + styled.div<{ bar: string }>` color: ${props => { - // $ExpectType { themed: "black" } - const { theme } = props - return theme.themed + // $ExpectType { theme?: any; } & ClassAttributes & HTMLAttributes & { bar: string; } + props + + return {} }}; ` } -{ - const myStyled: CreateStyled<{ themed: 'black' }> = styled - // $ExpectError - created styled shouldn't allow for parametrizing with Theme type - myStyled.div<{ bar: string }, { themed: 'orange' }>`` +const StyledDiv = styled.div({}) +; - myStyled.div<{ bar: string }>` - color: ${props => { - // $ExpectType { themed: "black" } - const { theme } = props - return theme.themed - }}; - ` -} +// can specify theme for StyledTags +const themedStyled = styled as CreateStyled<{ themeProp: string }> +const StyledDiv2 = themedStyled.div(props => { + // $ExpectType { themeProp: string; } + props.theme + + return {} +}) +; + +// Can override theme optionally on prop +; + +// $ExpectError +; + +const Container = styled.div((props: { test: number }) => ({ + width: props.test +})) + +const Container2 = styled.div<{ test: number }>(props => ({ + width: props.test +})) + +const Container3 = styled.div(({ theme }) => ({ + width: theme.width +})) diff --git a/packages/styled/types/tslint.json b/packages/styled/types/tslint.json index 58fc09cd9..265066d41 100644 --- a/packages/styled/types/tslint.json +++ b/packages/styled/types/tslint.json @@ -6,7 +6,18 @@ "generic" ], "semicolon": false, - + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-module", + "check-rest-spread", + "check-type", + "check-typecast", + "check-type-operator", + "check-preblock" + ], "no-unnecessary-generics": false } -} +} \ No newline at end of file diff --git a/packages/stylis/types/tslint.json b/packages/stylis/types/tslint.json index ad5ca2366..c425bd601 100644 --- a/packages/stylis/types/tslint.json +++ b/packages/stylis/types/tslint.json @@ -8,6 +8,8 @@ "generic" ], "semicolon": false, - "space-before-function-paren": false + "space-before-function-paren": false, + "no-null-undefined-union": false, + "callable-types": false } -} +} \ No newline at end of file diff --git a/packages/unitless/package.json b/packages/unitless/package.json index c1ff72015..250f31212 100644 --- a/packages/unitless/package.json +++ b/packages/unitless/package.json @@ -6,6 +6,9 @@ "module": "dist/unitless.esm.js", "license": "MIT", "repository": "https://github.com/emotion-js/emotion/tree/master/packages/unitless", + "scripts": { + "test:typescript": "exit 0" + }, "publishConfig": { "access": "public" }, diff --git a/packages/weak-memoize/types/index.d.ts b/packages/weak-memoize/types/index.d.ts index a2b9f0654..b5344615a 100644 --- a/packages/weak-memoize/types/index.d.ts +++ b/packages/weak-memoize/types/index.d.ts @@ -1,3 +1,5 @@ +// TypeScript Version: 2.2 + type UnaryFn = (arg: Arg) => Return export default function weakMemoize( diff --git a/packages/weak-memoize/types/tests.ts b/packages/weak-memoize/types/tests.ts index 72a869db9..8daa3243d 100644 --- a/packages/weak-memoize/types/tests.ts +++ b/packages/weak-memoize/types/tests.ts @@ -1,15 +1,17 @@ import weakMemoize from '@emotion/weak-memoize' -type Foo = { bar: 'xyz' } +interface Foo { + bar: 'xyz' +} declare class Qwe { answer: number } -// $ExpectType Array +// $ExpectType Foo[] weakMemoize((arg: Foo) => [arg])({ bar: 'xyz' }) -// $ExpectType string[] +// $ExpectError weakMemoize((arg: string) => [arg])('foo') // $ExpectError @@ -24,8 +26,6 @@ weakMemoize((arg: boolean) => [arg]) // $ExpectError weakMemoize((arg: symbol) => [arg]) // $ExpectError -weakMemoize((arg: void) => [arg]) -// $ExpectError weakMemoize((arg: null) => [arg]) // $ExpectError weakMemoize((arg: undefined) => [arg]) diff --git a/packages/weak-memoize/types/tslint.json b/packages/weak-memoize/types/tslint.json index b9706ef70..5da4cfaab 100644 --- a/packages/weak-memoize/types/tslint.json +++ b/packages/weak-memoize/types/tslint.json @@ -20,6 +20,7 @@ "check-preblock" ], - "no-unnecessary-generics": false + "no-unnecessary-generics": false, + "strict-export-declare-modifiers": false } -} +} \ No newline at end of file diff --git a/playgrounds/cra/package.json b/playgrounds/cra/package.json index a5494a2de..01c8e1127 100644 --- a/playgrounds/cra/package.json +++ b/playgrounds/cra/package.json @@ -11,6 +11,7 @@ "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "test:typescript": "exit 0" } } \ No newline at end of file diff --git a/playgrounds/razzle/package.json b/playgrounds/razzle/package.json index bbaf5e7f6..2f9d9b45a 100644 --- a/playgrounds/razzle/package.json +++ b/playgrounds/razzle/package.json @@ -7,7 +7,8 @@ "start": "razzle start", "build": "razzle build", "test": "razzle test --env=jsdom", - "start:prod": "NODE_ENV=production node build/server.js" + "start:prod": "NODE_ENV=production node build/server.js", + "test:typescript": "exit 0" }, "dependencies": { "express": "^4.16.3", diff --git a/scripts/babel-preset-emotion-dev/package.json b/scripts/babel-preset-emotion-dev/package.json index 1ffb991f7..445e456c8 100644 --- a/scripts/babel-preset-emotion-dev/package.json +++ b/scripts/babel-preset-emotion-dev/package.json @@ -3,6 +3,9 @@ "main": "src/index", "version": "9.2.6", "private": true, + "scripts": { + "test:typescript": "exit 0" + }, "dependencies": { "@babel/plugin-proposal-class-properties": "^7.5.5", "@babel/plugin-transform-flow-strip-types": "^7.4.4", diff --git a/scripts/babel-tester/package.json b/scripts/babel-tester/package.json index 0fab954a0..cc2226215 100644 --- a/scripts/babel-tester/package.json +++ b/scripts/babel-tester/package.json @@ -2,5 +2,8 @@ "name": "babel-tester", "main": "src/index.js", "version": "0.4.5", - "private": true + "private": true, + "scripts": { + "test:typescript": "exit 0" + } } diff --git a/scripts/benchmarks/package.json b/scripts/benchmarks/package.json index dcb62a22b..c1ed77ae2 100644 --- a/scripts/benchmarks/package.json +++ b/scripts/benchmarks/package.json @@ -5,7 +5,8 @@ "scripts": { "start": "parcel src/index.html", "build": "parcel build src/index.html --public-url .", - "run-benchmark": "node run.js" + "run-benchmark": "node run.js", + "test:typescript": "exit 0" }, "dependencies": { "@babel/core": "^7.5.5", diff --git a/scripts/old-babel-tester/package.json b/scripts/old-babel-tester/package.json index 7187b3a1d..0f5cd0a19 100644 --- a/scripts/old-babel-tester/package.json +++ b/scripts/old-babel-tester/package.json @@ -5,5 +5,8 @@ "private": true, "dependencies": { "babel-core": "^6.26.3" + }, + "scripts": { + "test:typescript": "exit 0" } } diff --git a/scripts/ssr-benchmarks/package.json b/scripts/ssr-benchmarks/package.json index c5158bfe8..33bf701e4 100644 --- a/scripts/ssr-benchmarks/package.json +++ b/scripts/ssr-benchmarks/package.json @@ -3,7 +3,8 @@ "name": "ssr-benchmarks", "version": "0.8.13", "scripts": { - "start": "NODE_ENV=production node bench.js" + "start": "NODE_ENV=production node bench.js", + "test:typescript": "exit 0" }, "dependencies": { "benchmark": "^2.1.4" diff --git a/scripts/test-utils/package.json b/scripts/test-utils/package.json index 9c92b8177..9b4f9c1fd 100644 --- a/scripts/test-utils/package.json +++ b/scripts/test-utils/package.json @@ -2,5 +2,8 @@ "name": "test-utils", "main": "src/index", "version": "0.3.2", - "private": true + "private": true, + "scripts": { + "test:typescript": "exit 0" + } } diff --git a/site/package.json b/site/package.json index 08a4275cb..1d36ffb52 100644 --- a/site/package.json +++ b/site/package.json @@ -51,6 +51,7 @@ "license": "MIT", "scripts": { "build:site": "gatsby build", - "start:site": "gatsby develop" + "start:site": "gatsby develop", + "test:typescript": "exit 0" } -} +} \ No newline at end of file