diff --git a/docs/src/pages/guides/typescript/typescript.md b/docs/src/pages/guides/typescript/typescript.md index 46708a793c74c9..c1f3af2382248c 100644 --- a/docs/src/pages/guides/typescript/typescript.md +++ b/docs/src/pages/guides/typescript/typescript.md @@ -56,45 +56,7 @@ const DecoratedClass = decorate( ); ``` -Note that in the class example you didn't need to annotate `` in the call to `decorate`; type inference took care of everything. However, there are 2 scenarios where you _do_ need to provide an explicit type argument to `decorate`. - -Scenario 1: your styled component takes _no_ additional props in addition to `classes`. The natural thing would be to write: - -```jsx -import { WithStyles } from 'material-ui/styles'; - -const DecoratedNoProps = decorate( - class extends React.Component> { - render() { - return ( - - Hello, World! - - ); - } - } -); -``` - -Unfortunately, TypeScript infers the wrong type in this case and you'll have trouble when you go to make an element of this component. In this case, you'll need to provide an explicit `{}` type argument, like so: - -```jsx -import { WithStyles } from 'material-ui/styles'; - -const DecoratedNoProps = decorate<{}>( // <-- note the type argument! - class extends React.Component> { - render() { - return ( - - Hello, World! - - ); - } - } -); -``` - -Scenario 2: `Props` is a union type. Again, to avoid getting a compiler error, you'll need to provide an explict type argument: +When your `props` are a union, Typescript needs you to explicitly tell it the type, by providing a generic `` parameter to `decorate`: ```jsx import { WithStyles } from 'material-ui/styles'; @@ -125,7 +87,6 @@ const DecoratedUnionProps = decorate( // <-- without the type argument, w ); ``` -To avoid worrying about these 2 edge cases, it may be a good habit to always provide an explicit type argument to `decorate`. ### Injecting Multiple Classes diff --git a/packages/material-ui/src/index.d.ts b/packages/material-ui/src/index.d.ts index 0e573b37b39be0..e7f7cf83210b1f 100644 --- a/packages/material-ui/src/index.d.ts +++ b/packages/material-ui/src/index.d.ts @@ -46,6 +46,9 @@ type Diff = ({ [P in T]: P } & /** @internal */ export type Omit = Pick>; +/** @internal */ +export type ConsistentWith = Partial & Record; + export namespace PropTypes { type Alignment = 'inherit' | 'left' | 'center' | 'right' | 'justify'; type Color = 'inherit' | 'primary' | 'secondary' | 'default'; diff --git a/packages/material-ui/src/styles/withStyles.d.ts b/packages/material-ui/src/styles/withStyles.d.ts index 7be0af180738f6..2a3bfbd85751df 100644 --- a/packages/material-ui/src/styles/withStyles.d.ts +++ b/packages/material-ui/src/styles/withStyles.d.ts @@ -1,4 +1,6 @@ import * as React from 'react'; +import { WithTheme } from '../styles/withTheme'; +import { ConsistentWith } from '..'; import { Theme } from './createMuiTheme'; import * as CSS from 'csstype'; @@ -34,9 +36,8 @@ export interface WithStylesOptions { export type ClassNameMap = Record; -export interface WithStyles { +export interface WithStyles extends Partial { classes: ClassNameMap; - theme?: Theme; } export interface StyledComponentProps { @@ -47,6 +48,11 @@ export interface StyledComponentProps { export default function withStyles( style: StyleRules | StyleRulesCallback, options?: WithStylesOptions, -):

( - component: React.ComponentType

>, -) => React.ComponentType

>; +): { + ( + component: React.ComponentType>, + ): React.ComponentType>; +

>>( + component: React.ComponentType

>, + ): React.ComponentType

>; +}; diff --git a/packages/material-ui/src/styles/withTheme.d.ts b/packages/material-ui/src/styles/withTheme.d.ts index ee01063b2512be..5a455a06dffe92 100644 --- a/packages/material-ui/src/styles/withTheme.d.ts +++ b/packages/material-ui/src/styles/withTheme.d.ts @@ -1,10 +1,11 @@ import { Theme } from './createMuiTheme'; +import { ConsistentWith } from '..'; export interface WithTheme { theme: Theme; } -declare const withTheme: () =>

( +declare const withTheme: () =>

>( component: React.ComponentType

, ) => React.ComponentClass

; diff --git a/packages/material-ui/src/utils/withWidth.d.ts b/packages/material-ui/src/utils/withWidth.d.ts index e031c3d41296ec..575bb67566c9c9 100644 --- a/packages/material-ui/src/utils/withWidth.d.ts +++ b/packages/material-ui/src/utils/withWidth.d.ts @@ -1,4 +1,5 @@ import { Breakpoint } from '../styles/createBreakpoints'; +import { ConsistentWith } from '..'; export interface WithWidthOptions { resizeInterval: number; @@ -22,6 +23,6 @@ export function isWidthUp( export default function withWidth( options?: WithWidthOptions, -):

( +):

>( component: React.ComponentType

, ) => React.ComponentClass

>; diff --git a/packages/material-ui/src/utils/withWidth.spec.tsx b/packages/material-ui/src/utils/withWidth.spec.tsx index db4d42b3b916ef..2ada0716a95c7c 100644 --- a/packages/material-ui/src/utils/withWidth.spec.tsx +++ b/packages/material-ui/src/utils/withWidth.spec.tsx @@ -34,3 +34,10 @@ export class Hello extends React.Component; + +const WidthSFC = withWidth()<{ + // shouldn't need to specify width here; it's a given + name: string; +}>(({ width, name }) =>

hello, {name}
); + +; diff --git a/packages/material-ui/test/typescript/components.spec.tsx b/packages/material-ui/test/typescript/components.spec.tsx index 851755d6d29ad0..f0280f1163ba55 100644 --- a/packages/material-ui/test/typescript/components.spec.tsx +++ b/packages/material-ui/test/typescript/components.spec.tsx @@ -718,7 +718,7 @@ const TableTest = () => { ); } - return withStyles(styles)<{}>(BasicTable); + return withStyles(styles)(BasicTable); }; const TabsTest = () => { diff --git a/packages/material-ui/test/typescript/styles.spec.tsx b/packages/material-ui/test/typescript/styles.spec.tsx index 8cd28be647fb8c..ce43caa0002fb3 100644 --- a/packages/material-ui/test/typescript/styles.spec.tsx +++ b/packages/material-ui/test/typescript/styles.spec.tsx @@ -57,7 +57,7 @@ const ComponentWithChildren: React.SFC> = ({ children, }) =>
{children}
; -const StyledExampleThree = withStyles(styleRule)<{}>(ComponentWithChildren); +const StyledExampleThree = withStyles(styleRule)(ComponentWithChildren); ; // Also works with a plain object diff --git a/packages/material-ui/test/typescript/styling-comparison.spec.tsx b/packages/material-ui/test/typescript/styling-comparison.spec.tsx index 45ef738fdb105c..c885436463bdd3 100644 --- a/packages/material-ui/test/typescript/styling-comparison.spec.tsx +++ b/packages/material-ui/test/typescript/styling-comparison.spec.tsx @@ -35,7 +35,7 @@ const DecoratedClass = decorate( }, ); -const DecoratedNoProps = decorate<{}>( +const DecoratedNoProps = decorate( class extends React.Component> { render() { return Hello, World!; @@ -46,3 +46,31 @@ const DecoratedNoProps = decorate<{}>( const sfcElem = ; const classElem = ; const noPropsElem = ; + +interface Book { + category: 'book'; + author: string; +} + +interface Painting { + category: 'painting'; + artist: string; +} + +type ArtProps = Book | Painting; + +const DecoratedUnionProps = decorate( // <-- without the type argument, we'd get a compiler error! + class extends React.Component> { + render() { + const props = this.props; + return ( + + {props.category === 'book' ? props.author : props.artist} + + ); + } + }, +); + +const unionPropElem = ; +