Rewrite defaultProps support in styled-components

This commit is contained in:
Igor Oleinikov 2019-01-16 00:18:33 -08:00
parent 5f012ded23
commit 024685f00e
2 changed files with 40 additions and 6 deletions

View File

@ -32,6 +32,21 @@ export interface ThemeProps<T> {
export type ThemedStyledProps<P, T> = P & ThemeProps<T>;
export type StyledProps<P> = ThemedStyledProps<P, AnyIfEmpty<DefaultTheme>>;
// Any prop that has a default prop becomes optional, but its type is unchanged
// Undeclared default props are augmented into the resulting allowable attributes
// If declared props have indexed properties, ignore default props entirely as keyof gets widened
// Wrap in an outer-level conditional type to allow distribution over props that are unions
type Defaultize<P, D> = P extends any
? string extends keyof P ? P :
& Pick<P, Exclude<keyof P, keyof D>>
& Partial<Pick<P, Extract<keyof P, keyof D>>>
& Partial<Pick<D, Exclude<keyof D, keyof P>>>
: never;
type ReactDefaultizedProps<C, P> = C extends { defaultProps: infer D; }
? Defaultize<P, D>
: P;
export type StyledComponentProps<
// The Component from whose props are derived
C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
@ -42,8 +57,13 @@ export type StyledComponentProps<
// The props that are made optional by .attrs
A extends keyof any
> = WithOptionalTheme<
Omit<React.ComponentPropsWithRef<C> & O, A> &
Partial<Pick<React.ComponentPropsWithRef<C> & O, A>>,
Omit<
ReactDefaultizedProps<
C,
React.ComponentPropsWithRef<C>
> & O,
A
> & Partial<Pick<React.ComponentPropsWithRef<C> & O, A>>,
T
>;
@ -156,8 +176,6 @@ export interface StyledComponentBase<
}
): React.ReactElement<StyledComponentProps<C, T, O, A>>;
readonly defaultProps: ReactDefaultProps<C>;
withComponent<WithC extends AnyStyledComponent>(
component: WithC
): StyledComponent<

View File

@ -978,9 +978,25 @@ function validateDefaultProps() {
<StyledComponent requiredProp optionalProp="x" />;
// this test is failing in TS 3.0 but not in 3.1
// <StyledComponent requiredProp />;
<StyledComponent requiredProp />;
// still respects the type of optionalProp
<StyledComponent requiredProp optionalProp={1} />; // $ExpectError
// example of a simple helper that sets defaultProps and update the type
type WithDefaultProps<C, D> = C & { defaultProps: D };
function withDefaultProps<C, D>(component: C, defaultProps: D): WithDefaultProps<C, D> {
(component as WithDefaultProps<C, D>).defaultProps = defaultProps;
return component as WithDefaultProps<C, D>;
}
const OtherStyledComponent = withDefaultProps(
styled(MyComponent)` color: red `,
{ requiredProp: true }
);
// this test is failing in TS 3.1 but not in 3.2
// <OtherStyledComponent />;
<OtherStyledComponent requiredProp="1" />; // $ExpectError
}