Flow: $Diff with ...rest doesn't work

Created on 19 Mar 2017  路  9Comments  路  Source: facebook/flow

Example:

/* @flow */

type Props = {
    title: string;
    age: number;
};

type WrapProps = $Diff<Props, {
    age: number;
}>;

const Person = (props: Props) => {};
const YoungPerson = (props: WrapProps) => {
    return Person({age: 21, ...props});
};

Gives an error:

14:     return Person({age: 21, ...props});
                   ^ object literal. Expected object instead of
14:     return Person({age: 21, ...props});
                                ^ Props

However if i declare WrapProps sub type manually, it works:

/* @flow */

type Props = {
    title: string;
    age: number;
};

/*type WrapProps = $Diff<Props, {
    age: number;
}>;
*/

type WrapProps = {
    title: string;
};

const Person = (props: Props) => {};
const YoungPerson = (props: WrapProps) => {
    return Person({age: 21, ...props});
};

Seems like $Diff is not working correctly.

destructors

Most helpful comment

Here is as simple example:

type X = $Diff<{ a: string}, {}>;
var x: X = { a: 'string' }; // This works as expected
x.a; // This is an error

Each type consists of two parts: upper bound and lower bound. Upper bound defines if you can assign something to the type (i.e. if it's a subtype of the type). Lower bound defines if you can use the type in some particular way. $Diff only implements upper bound, so once you have something of type $Diff you can't do anything with it.

So if you want to use $Diff in your code you have to cast it to any. This is probably fine if you take $Diff as an argument, but not ideal if you return it from a function.

All 9 comments

Indeed, you can't use the spread type on a $Diff type either:

type SpreadFromDiff = {...$Diff<Props, {title: string}>}

$Diff is not a public feature. It only works properly as lower bound, i.e. you can assign something to it, but can't use it after that

but the first example should work, right ?
i mean when using rest on the actual value that has been typed with $Diff

No, it shouldn't. $Diff only works properly as argument in declarations

@vkurchatkin it's publicly documented https://flow.org/en/docs/types/utilities/#toc-diff and I don't see how that example is different from the example in this issues (sans the ... spread).

It would be great if you can provide an example of how it can be used since I've tried $Diff multiple times without success and I'm afraid I don't follow any of the explanations above :(

$Diff aside, is there any other idiomatic way to annotate react components that wrap another component and exposing a subset of the wrapped component's props (i.e. providing the rest itself inside the wrapper)?

Edit
(Without declaring all the re-exposed props, which might be many and complex)

Here is as simple example:

type X = $Diff<{ a: string}, {}>;
var x: X = { a: 'string' }; // This works as expected
x.a; // This is an error

Each type consists of two parts: upper bound and lower bound. Upper bound defines if you can assign something to the type (i.e. if it's a subtype of the type). Lower bound defines if you can use the type in some particular way. $Diff only implements upper bound, so once you have something of type $Diff you can't do anything with it.

So if you want to use $Diff in your code you have to cast it to any. This is probably fine if you take $Diff as an argument, but not ideal if you return it from a function.

@vkurchatkin thanks for the example, that clears things up, really helpful!

Just a detail, in the other post above you wrote "lower bound" and this reply "upper bound" (which matches your explanation for $Diff)

Issue described by @vkurchatkin above doesn't seem to be an issue any longer since flow 0.57

Was this page helpful?
0 / 5 - 0 ratings