$Shape<T> does not error on missing properties: think of $Shape<T> and T as "overlapping" types. Consider using a sealed object type or $Exact<T>.
I interpret $Shape<{a: string}> as {a?: string}, where am I wrong?
type A = {a: string};
function test(a: $Shape<A>) {
a.a.repeat(2); // no error
}
Is it the expected behavior?
i've found that wrapping $Shape<T> in object spread like this fixes the problem, so right now this: {...$Shape<{a: string}>} works like this: {a?: string}
I'm not sure if this is intended behaviour, only with spread it fulfills conditions written in docs:
Copies the shape of the type supplied, but marks every field optional.
your working example:
type A = {a: string};
type SpreadShape = {
...$Shape<A>,
}
function test(a: SpreadShape) {
a.a.repeat(2); // Cannot call `a.a.repeat` because property `repeat` is missing in undefined [1].
}
// References:
// 1: type A = {a: string};
Am i missing something? If not, maybe we should update the docs?
@jacekkolasa I'm afraid your workaround stopped working in version 0.106.0. It is still working with 0.105.2:
https://flow.org/try/#0C4TwDgpgBAglC8UDeBDAXFAzsATgSwDsBzAXwG4AoUSKAZTBwhQBNaALFGxJCqKAOkEASdpwgAeGAD4ANBRIUAZgFcCAY2B4A9gSjAI2ABTo6DJqw6QAlMl5QU-B40gpghgExWyUAPQ+oAMIoBARawFBqKAA2UVAABg5OEC7AcVAARhCRypjQDFqQOKDxzkypUHiYUAC2lZiERBW6qswQioQQzFAA2gCMALr88kA
It's also not working at all when using strict objects, which we are almost exclusively doing...
A lot of valuable discussion is going on in https://github.com/facebook/flow/issues/7566, and I think this solution is what will work in most cases: https://github.com/facebook/flow/issues/7566#issuecomment-526324094
Here is another example of $Rest<T, {} working as expected, even with strict objects:
There is even a good explanation in the docs:
https://flow.org/en/docs/types/utilities/#toc-rest
For example, $Rest<{|n: number|}, {}> will result in {|n?: number|} because an in-exact empty object may have an n property
At least this is what we are going with at the moment.
There's also a weird error when treating $Rest<T, {}> like $Shape: https://flow.org/try/#0PTAEAEDMBsHsHcBQiAuBPADgU1ABQIYBOKAlvtADwAqAfKALygAkASlgM4rUA0oA3gF8aiUKFSYcAZVgBbLFQkN+AH1D4AXKE6ESAOwDmoZQOQBjWLs6gsAD3ymUm6XIXYlfNZoDkX0CcTmliigGESk5JoExGSUzvISdIweGqA+fmYWVliEhLCEkWExFHGuWInWdg5AA
... because string [1] is incompatible with undefined [1] ...
just looks plain wrong, as [1] is talking about the same property, yet one of them is string, the other is undefined
@loyd you would hope $Shape is the same as making all the properties optional, but it isn't unfortunately.
const b: $ReadOnly<{age?: number}> = { name: 'foo' }
const a: $ReadOnly<$Shape<{age: number}>> = { name: 'foo' } // Cannot assign object literal to `a` because property `age` is missing in object literal
So what's the actual purpose of $Shape? Who knows...
As the docs say,
Note: $Shape
is not equivalent to T with all its fields marked as optional. In particular, Flow unsoundly allows $Shape to be used as a T in several contexts.
I don't think anyone but people who know the internals of Flow really understands how $Shape behaves. It's not a good type.
So we've created $MaybeKeys<T> = $Rest<T, {}> as a global in one project. What is $Shape<T> actually supposed to do?
Most helpful comment
I interpret
$Shape<{a: string}>as{a?: string}, where am I wrong?