Flow: $Shape doesn't work

Created on 23 Jan 2018  路  10Comments  路  Source: facebook/flow

type A = {a: string};

function test(fn: $Shape<A> => void): void { fn({}); }

function fn(a: A): void { a.a.slice(2) }

test(fn)   // no error 0_o

Try it

destructors

Most helpful comment

I interpret $Shape<{a: string}> as {a?: string}, where am I wrong?

All 10 comments

$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:

https://flow.org/try/#0C4TwDgpgBAChBOBnA9gOygXigbwD4CgooBDAcwgC4pUBXAWwCMEAaQ64uyqRYeAS1SlWuAL758oSLGLxgfYgBsAPABUAfJigASAEoQeq5jhFr8AYzQ8oYGXMVwkaKjFvzlDlKg1ZsJclQAWACYoMXwbWTcPNAA6VA4IGPgISGJgAAoggEooAHpcqGAACz5EKAt4ZLNgBRBC8AgAWgR4ZCRxCLsFaNQYgDNkZDyCnj4FBWpkYBIzM31EPgYFCCMmM2IaRGgeqFKoCAAPYmqgA

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?

Was this page helpful?
0 / 5 - 0 ratings