facebook / flow

Adds static typing to JavaScript to improve developer productivity and code quality.
https://flow.org/
MIT License
22.08k stars 1.85k forks source link

Wrong behavior after usage of object rest operator #5548

Closed apsavin closed 1 month ago

apsavin commented 6 years ago
type TypeWithOptionalProps = {
    requiredProp: string,
    optionalProp?: string,
}

const obj: TypeWithOptionalProps = { requiredProp: 'string', optionalProp: 'string' };
const { optionalProp = '', ...clearedObj } = obj;

(clearedObj: TypeWithOptionalProps);

const objForCheck: TypeWithOptionalProps = { requiredProp: 'string' };

Expected result: no errors Actual result: error on line (clearedObj: TypeWithOptionalProps);

You can reproduce it here

Introduced in 0.60 (works correctly in 0.59)

calebmer commented 6 years ago

This is the expected behavior 😊

Consider:

const x: {a: number, b: boolean} = {a: 42, b: true};
const y: {a: number} = x;
const z: {a: number, b?: string} = y;

try-Flow

TypeScript allows this, but Flow does not because it is unsound!

It’s a little hard to see that behavior in this case since the rest object we know absolutely doesn’t have that property. Flow currently doesn’t have any special type for “object which never had property p.” We are working on some spread + rest refactors. We may introduce this functionality during that.

For now you can use $Diff<TypeWithOptionalProps, {optionalProp: string | void}> to create a type without the optional prop 😊

Going to leave this open. While it’s the expectedbehavior for now, we could improve object rest typing to support this 👍

apsavin commented 6 years ago

Thanks for the answer!

we could improve object rest typing to support this

If it's possible then it should be done: it's obvious for a developer that the code in my example can't lead to errors (while code in your example obviously can).

anru commented 6 years ago

@calebmer ok, but why this error is still happens on exact objects ?

See this example ( TRY )

// @flow

type A = {|
  a: number,
  b: number,
|}

type C = {
  a: number,
  c?: number,
}

let a: A = {a: 1, b: 2};

const { b, ...restA } = a; // restA == {| a: 1 |}

const c: C = restA; // Why error ?

As we can see, restA can't have more properties than just a, but flow complains about restA does not satisfy type C

anru commented 6 years ago

if anyone are interested, found a workaround for example above

SamChou19815 commented 1 month ago

it now works