From bdcabd860e7e1d3d4eeb09a792ce794b4b21c883 Mon Sep 17 00:00:00 2001 From: Roy Art Date: Mon, 30 Mar 2020 16:53:27 +0200 Subject: [PATCH] [Card] Fix TypeScript not recognizing "component" prop (#20179) --- .../src/CardHeader/CardHeader.d.ts | 67 +++- .../src/CardHeader/CardHeader.spec.tsx | 299 ++++++++++++++++++ 2 files changed, 357 insertions(+), 9 deletions(-) create mode 100644 packages/material-ui/src/CardHeader/CardHeader.spec.tsx diff --git a/packages/material-ui/src/CardHeader/CardHeader.d.ts b/packages/material-ui/src/CardHeader/CardHeader.d.ts index 0a3c3f564e2f8b..61873bb281abc5 100644 --- a/packages/material-ui/src/CardHeader/CardHeader.d.ts +++ b/packages/material-ui/src/CardHeader/CardHeader.d.ts @@ -2,17 +2,28 @@ import * as React from 'react'; import { TypographyProps } from '../Typography'; import { OverridableComponent, OverrideProps } from '../OverridableComponent'; -export interface CardHeaderTypeMap

{ - props: P & { +export interface CardHeaderTypeMap< + Props = {}, + DefaultComponent extends React.ElementType = 'div', + TitleTypographyComponent extends React.ElementType = 'span', + SubheaderTypographyComponent extends React.ElementType = 'span' +> { + props: Props & { action?: React.ReactNode; avatar?: React.ReactNode; disableTypography?: boolean; subheader?: React.ReactNode; - subheaderTypographyProps?: Partial; + subheaderTypographyProps?: TypographyProps< + SubheaderTypographyComponent, + { component?: SubheaderTypographyComponent } + >; title?: React.ReactNode; - titleTypographyProps?: Partial; + titleTypographyProps?: TypographyProps< + TitleTypographyComponent, + { component?: TitleTypographyComponent } + >; }; - defaultComponent: D; + defaultComponent: DefaultComponent; classKey: CardHeaderClassKey; } /** @@ -25,13 +36,51 @@ export interface CardHeaderTypeMap

* * - [CardHeader API](https://material-ui.com/api/card-header/) */ -declare const CardHeader: OverridableComponent; +declare const CardHeader: OverridableCardHeader; + +export interface OverridableCardHeader extends OverridableComponent { + < + DefaultComponent extends React.ElementType = CardHeaderTypeMap['defaultComponent'], + Props = {}, + TitleTypographyComponent extends React.ElementType = 'span', + SubheaderTypographyComponent extends React.ElementType = 'span' + >( + props: CardHeaderPropsWithComponent< + DefaultComponent, + Props, + TitleTypographyComponent, + SubheaderTypographyComponent + >, + ): JSX.Element; +} export type CardHeaderClassKey = 'root' | 'avatar' | 'action' | 'content' | 'title' | 'subheader'; export type CardHeaderProps< - D extends React.ElementType = CardHeaderTypeMap['defaultComponent'], - P = {} -> = OverrideProps, D>; + DefaultComponent extends React.ElementType = CardHeaderTypeMap['defaultComponent'], + Props = {}, + TitleTypographyComponent extends React.ElementType = 'span', + SubheaderTypographyComponent extends React.ElementType = 'span' +> = OverrideProps< + CardHeaderTypeMap< + Props, + DefaultComponent, + TitleTypographyComponent, + SubheaderTypographyComponent + >, + DefaultComponent +>; + +export type CardHeaderPropsWithComponent< + DefaultComponent extends React.ElementType = CardHeaderTypeMap['defaultComponent'], + Props = {}, + TitleTypographyComponent extends React.ElementType = 'span', + SubheaderTypographyComponent extends React.ElementType = 'span' +> = { component?: DefaultComponent } & CardHeaderProps< + DefaultComponent, + Props, + TitleTypographyComponent, + SubheaderTypographyComponent +>; export default CardHeader; diff --git a/packages/material-ui/src/CardHeader/CardHeader.spec.tsx b/packages/material-ui/src/CardHeader/CardHeader.spec.tsx new file mode 100644 index 00000000000000..98e49411350ecb --- /dev/null +++ b/packages/material-ui/src/CardHeader/CardHeader.spec.tsx @@ -0,0 +1,299 @@ +import * as React from 'react'; +import CardHeader, { CardHeaderProps, CardHeaderTypeMap } from '@material-ui/core/CardHeader'; + +const CustomComponent: React.FC<{ stringProp: string; numberProp: number }> = () =>

; + +type DefaultComponent = CardHeaderTypeMap['defaultComponent']; + +interface ComponentProp { + component?: React.ElementType; +} + +function createElementBasePropMixedTest() { + React.createElement>(CardHeader); + React.createElement>(CardHeader, { + component: 'div', + }); + // ExpectError: type system should be demanding the required props of "CustomComponent" + React.createElement>(CardHeader, { + component: CustomComponent, + }); + // $ExpectError + React.createElement>(CardHeader, { + // This test shouldn't fail but does; stringProp & numberProp are required props of CustomComponent + component: CustomComponent, + stringProp: '', + numberProp: 0, + }); + React.createElement(CardHeader, { + disableTypography: true, + }); + // $ExpectError + React.createElement>(CardHeader, { + unknownProp: 'shouldNotWork', + }); + // $ExpectError + React.createElement(CardHeader, { + disableTypography: 'hello', + }); + // $ExpectError + React.createElement(CardHeader, { + disableTypography: 1, + }); + // $ExpectError + React.createElement>(CardHeader, { + component: 'incorrectElement', + }); +} + +function createElementTypographyTest() { + React.createElement(CardHeader, { + titleTypographyProps: { + align: 'center', + }, + }); + // $ExpectError + React.createElement(CardHeader, { + titleTypographyProps: { + align: 'incorrectAlign', + }, + }); + React.createElement(CardHeader, { + titleTypographyProps: { + variant: 'body1', + }, + }); + // $ExpectError + React.createElement(CardHeader, { + titleTypographyProps: { + variant: 123, + }, + }); + React.createElement>(CardHeader, { + titleTypographyProps: { + component: 'div', + }, + }); + // ExpectError: This is expected to err; the type system should catch required props from "CustomComponent". + React.createElement>(CardHeader, { + titleTypographyProps: { + component: CustomComponent, + }, + }); + React.createElement>(CardHeader, { + titleTypographyProps: { + component: CustomComponent, + stringProp: '', + numberProp: 0, + }, + }); + // ExpectError: This is expected to err; the type system should catch the props type mismatch + // from "CustomComponent" props. + React.createElement>(CardHeader, { + titleTypographyProps: { + component: CustomComponent, + stringProp: 0, + numberProp: '', + }, + }); + // ExpectError: This is expected to err; the type system is welcoming unknown props. + React.createElement>(CardHeader, { + titleTypographyProps: { + unknownProp: 'shouldNotWork', + }, + }); + // $ExpectError + React.createElement>(CardHeader, { + titleTypographyProps: { + component: 'incorrectComponent', + }, + }); + // $ExpectError + React.createElement(CardHeader, { + titleTypographyProps: true, + }); +} + +function componentPropTest() { + ; + ; + // $ExpectError + ; + // $ExpectError + ; +} + +function mixedCardHeaderComponentAndTypographyTest() { + ; + ; + ; + ; + // $ExpectError + ; + // $ExpectError + ; + ; + // $ExpectError + ; + ; +} + +function titleTypographyPropsTest() { + // $ExpectError + ; + ; + ; + ; + ; + ; + // $ExpectError + ; + // $ExpectError + ; + ; +} + +function subheaderTypographyPropsTest() { + ; + ; + ; + ; + ; + ; + // $ExpectError + ; + // $ExpectError + ; +} + +function mixedTypographyPropsTest() { + ; + ; + // $ExpectError + ; + ; + // $ExpectError + ; + ; + ; +}