php-standard-library / phpstan-extension

14 stars 4 forks source link

allow_unknown_fields is not taken into account #12

Open JeroenBakker opened 1 year ago

JeroenBakker commented 1 year ago

Hi there,

I often parse things such as API responses or other JSON decoded payloads with the allow_unknown_fields parameter set to true. This allows me to be forwards compatible with any fields that may be added in the future, or lets me assert the rest of a shape conditionally based on other data.

But PHPStan thinks it knows the whole shape of an array with this code:

Type\shape(['foo' => Type\string()], allow_unknown_fields: true)->assert($body);

\PHPStan\dumpType($body); // Dumped type: array{foo: string}

// Call to method Psl\Type\TypeInterface<array<string, string>>::matches() with array{foo: string} will always evaluate to false. 
if (Type\shape(['bar' => Type\string()], allow_unknown_fields: true)->matches($body)) {
    \PHPStan\dumpType($body); // Dumped type: *NEVER* 
}

I would expect a type like array&hasOffsetValue('foo', string) instead.

I tried digging into the code to see if I could offer a PR to add/fix this, but I have no clue how to achieve this 😅

JeroenBakker commented 1 year ago

Just wanted to mention that I was able to work around this by explicitly including any other fields I want to use later on as optional in the original assert (which seems a pretty obvious workaround in hindsight):

Type\shape([
    'foo' => Type\string(),
    'bar' => Type\optional(Type\string()),
], allow_unknown_fields: true)->assert($responseBody);

\PHPStan\dumpType($body); // Dumped type: array{foo: string, bar?: string}

But as the structure of the data gets more complex and contains many fields this would become very verbose contain a lot of duplication in all the checks.

But for now this works okay for me.