-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React component props: mixing defaultProps
and union types
#25503
Comments
I'm assuming this is because the way "outer props" type of a component is calculated uses |
This issue doesn't just effect components with default props, but also functions that have named props with defaults. Here is a complete example that demonstrates the problem. I'm using functions here instead of components for simplicity. (Components are just functions.) I think what we're missing is something like // `Omit` and `Diff` copied from
// https://github.com/gcanti/typelevel-ts/blob/e1d718db887476f7b6b6e2c88542cc6ce3bd8265/src/index.ts#L9-L13
type Omit<A extends object, K extends string | number | symbol> = Pick<A, Exclude<keyof A, K>>;
type Diff<A extends object, K extends keyof A> = Omit<A, K> & Partial<Pick<A, K>>;
type Props = { shared: string } & (
| {
type: 'foo';
foo: string;
}
| {
type: 'bar';
bar: string;
});
const defaultProps = {
shared: 'yay',
};
type DefaultProps = typeof defaultProps;
type InputProps = Diff<Props, keyof DefaultProps>;
const component = (props: Props) => {};
const componentWithDefaults = (inputProps: InputProps) => {
/* Type '{ type: "foo" | "bar"; shared: string; }' is not assignable to type 'Props'.
Type '{ type: "foo" | "bar"; shared: string; }' is not assignable to type '{ shared: string; } & { type: "bar"; bar: string; }'.
Type '{ type: "foo" | "bar"; shared: string; }' is not assignable to type '{ type: "bar"; bar: string; }'.
Property 'bar' is missing in type '{ type: "foo" | "bar"; shared: string; }'. */
const props: Props = { ...defaultProps, ...inputProps }
return component(props);
};
componentWithDefaults({
type: 'foo',
/* Argument of type '{ type: "foo"; foo: string; }' is not assignable to parameter of type 'Diff<Props, "shared">'.
Object literal may only specify known properties, and 'foo' does not exist in type 'Diff<Props, "shared">'. */
foo: 'foo',
}) |
Ah, this is possible to fix if we rewrite the type Omit<T, K extends keyof T> = T extends any ? Pick<T, Exclude<keyof T, K>> : never Using this knowledge we can rewrite type Defaultize<TProps, TDefaults> = TProps extends any
? { [K in Extract<keyof TProps, keyof TDefaults>]?: TProps[K] } &
{ [K in Exclude<keyof TProps, keyof TDefaults>]: TProps[K] } &
Partial<TDefaults>
: never; Then we get the expected behaviour: <Component type="foo" />; // Expected error, got one :-)
<Component type="foo" foo="1" />; // No error, as expected :-) |
TypeScript Version: 2.9.2
Search Terms: react component props union types defaultprops
Code
Using the new support for
defaultProps
in TypeScript 3.0 re. #24422#24422 (comment)
/cc @mhegazy who requested that I open an issue to discuss/track this.
The text was updated successfully, but these errors were encountered: