Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Psalm complains when passing an array with extra properties #5379

Closed
BenMorel opened this issue Mar 12, 2021 · 8 comments
Closed

Psalm complains when passing an array with extra properties #5379

BenMorel opened this issue Mar 12, 2021 · 8 comments

Comments

@BenMorel
Copy link
Contributor

BenMorel commented Mar 12, 2021

Repro: https://psalm.dev/r/440c1cd447

/**
 * @psalm-template K
 * @psalm-template V
 */
class Collection {}

/**
 * @psalm-param Collection<int, array{code: string}> $items
 */
function test(Collection $items): void {}

/** @var Collection<int, array{code: string, value: string}> $value */
test($value);

Result:

ERROR: InvalidArgument - 17:6 - Argument 1 of test expects Collection<int, array{code: string}>, Collection<int, array{code: string, value: string}> provided

If I change Collection for an array, it works fine: https://psalm.dev/r/0ae6a8d4a3

@psalm-github-bot
Copy link

psalm-github-bot bot commented Mar 12, 2021

I found these snippets:

https://psalm.dev/r/440c1cd447
<?php

/**
 * @psalm-template K
 * @psalm-template V
 */
class Collection {}

/**
 * @psalm-suppress UnusedParam
 * @psalm-param Collection<int, array{code: string}> $items
 */
function test(Collection $items): void {}

/** @var Collection<int, array{code: string, value: string}> $value */

test($value);
Psalm output (using commit 3817193):

ERROR: InvalidArgument - 17:6 - Argument 1 of test expects Collection<int, array{code: string}>, Collection<int, array{code: string, value: string}> provided
https://psalm.dev/r/0ae6a8d4a3
<?php

/**
 * @psalm-suppress UnusedParam
 * @psalm-param array<int, array{code: string}> $items
 */
function test(array $items): void {}

/** @var array<int, array{code: string, value: string}> $value */

test($value);
Psalm output (using commit 3817193):

No issues!

@weirdan
Copy link
Collaborator

weirdan commented Mar 12, 2021

Generics are invariant, so you cannot pass Collection of a subtype where Collection of a supertype is expected. You would have to mark corresponding type parameter covariant for that to work: https://psalm.dev/r/015291e4ca

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/015291e4ca
<?php

/**
 * @psalm-template K
 * @psalm-template-covariant V
 */
class Collection {}

/**
 * @psalm-suppress UnusedParam
 * @psalm-param Collection<int, array{code: string}> $items
 */
function test(Collection $items): void {}

/** @var Collection<int, array{code: string, value: string}> $value */

test($value);
Psalm output (using commit 90fd1c5):

No issues!

@weirdan weirdan closed this as completed Mar 12, 2021
@BenMorel
Copy link
Contributor Author

Thanks for the pointer! I don't understand why generics are not covariant by default, though: what's a use case when covariance would not be desirable?

@weirdan
Copy link
Collaborator

weirdan commented Mar 12, 2021

Because of this: #1603

@weirdan
Copy link
Collaborator

weirdan commented Mar 13, 2021

The same example is also discussed in the docs: https://psalm.dev/docs/annotating_code/templated_annotations/#template-covariance

@BenMorel
Copy link
Contributor Author

Thank you, I understand the issue now. But can't the covariance / contravariance be inferred from the use case though: when it's used as a parameter or as a return type?

Why does it works with plain arrays?

@weirdan
Copy link
Collaborator

weirdan commented Mar 13, 2021

Array that you receive as a parameter is copied once you change it. It does not affect the array that was passed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants