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

Intersection with an imported type #5148

Open
egst opened this issue Feb 3, 2021 · 11 comments
Open

Intersection with an imported type #5148

egst opened this issue Feb 3, 2021 · 11 comments

Comments

@egst
Copy link

egst commented Feb 3, 2021

https://psalm.dev/r/ec2d56f41d

When I try to make an intersection type with an imported type, I get the following error:

Intersection types must be all objects or all object-like arrays, Psalm\Type\Atomic\TKeyedArray provided

Isn't TKeyedArray an object-like array? This seems like a bug to me, but maybe I'm not getting something right.

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/ec2d56f41d
<?php

/** @psalm-type X = array{x: int} */
class A {}

/** @psalm-import-type X from A
    @psalm-type Y = array{y: int}
    @psalm-type Q = X&Y */
class B {}
Psalm output (using commit 03665b9):

ERROR: InvalidDocblock - 9:1 - Intersection types must be all objects or all object-like arrays, Psalm\Type\Atomic\TKeyedArray provided

@egst
Copy link
Author

egst commented Feb 4, 2021

A possible workaround would be to introduce a dummy helper class with the second type definition and to import both types and only then perform the intersection on them.

https://psalm.dev/r/8ea579671b

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/8ea579671b
<?php

/** @psalm-type X = array{x: int} */
class A {}

/** @psalm-type Y = array{y: int} */
class _ {}
/** @psalm-import-type X from A
    @psalm-import-type Y from _
    @psalm-type Q = X&Y */
class B {}
Psalm output (using commit ff325b3):

No issues!

@douggr
Copy link

douggr commented Mar 17, 2021

A possible workaround would be to introduce a dummy helper class with the second type definition and to import both types and only then perform the intersection on them.

https://psalm.dev/r/8ea579671b

yet, it doesn't work if you try to use Q within types or returns; or am I missing something here?

https://psalm.dev/r/1eaa0c447f

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/8ea579671b
<?php

/** @psalm-type X = array{x: int} */
class A {}

/** @psalm-type Y = array{y: int} */
class _ {}
/** @psalm-import-type X from A
    @psalm-import-type Y from _
    @psalm-type Q = X&Y */
class B {}
Psalm output (using commit 3046468):

No issues!
https://psalm.dev/r/1eaa0c447f
<?php declare(strict_types=1);

/** @psalm-type X = array{x: int} */
class A {
    /** @var X */
    protected $foo = ['x' => 10];
}

/** @psalm-type Y = array{y: int} */
class _ {
    /** @var Y */
    protected $foo = ['y' => 10];
}

/**
 * @psalm-import-type X from A
 * @psalm-import-type Y from _
 * @psalm-type Q = X&Y
 */
class B {
    /** @var X */
    protected $x = ['x' => 10];

    /** @var Y */
    protected $y = ['y' => 10];

    /** @var Q */
    protected $q = ['x' => 1, 'y' => 2];

    /** @psalm-return Q */
    function getQ(): array
    {
        return $this->q;
    }

    /** @psalm-param Q $input */
    function setQ(array $input): void
    {
        $this->q = $input;
    }
}
Psalm output (using commit 3046468):

ERROR: InvalidDocblock - 31:5 - Invalid type alias X provided in docblock for B::getQ

ERROR: InvalidDocblock - 36:22 - Invalid type alias X provided in docblock for B::setQ

INFO: MixedPropertyTypeCoercion - 39:20 - $this->q expects 'array{x: int}',  parent type `array<array-key, mixed>` provided

ERROR: InvalidDocblock - 28:5 - Q is not a valid type (from /var/www/vhosts/psalm.dev/httpdocs/src/somefile.php:27)

@orklah
Copy link
Collaborator

orklah commented Oct 23, 2021

Error was clarified, intersections with array shapes are not supported

@orklah orklah closed this as completed Oct 23, 2021
@egst
Copy link
Author

egst commented Oct 25, 2021

@orklah, but intersections with array shapes are supported, aren't they? It's just the intersections with imported array shape types that cause problems. And why would you actually not support intersections with imported types? It just feels like a natural thing. It's like if you could include a file in PHP bud you couldn't call included functions inside of other functions and the PHP developer's solution would be to just clarify the error that it is not supported.

@orklah
Copy link
Collaborator

orklah commented Oct 25, 2021

You're right, I was confused, the basic version works, but here, the second example doesn't either: https://psalm.dev/r/9ddbcacb2f

The error is still kinda misleading. I'll flag this as enhancement

@orklah orklah reopened this Oct 25, 2021
@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/9ddbcacb2f
<?php

/**
 * @psalm-param array{a: string}&array{b: int} $_data
 */
function intersect(array $_data) :void {
  
}

/**
 * @psalm-param T as array{b: string}
 * @psalm-param array{a: string}&T $_data
 */
function intersect2(array $_data) :void {
  
}
Psalm output (using commit 4c0fbad):

ERROR: InvalidDocblock - 12:17 - Intersection types must be all objects, Psalm\Type\Atomic\TKeyedArray provided in docblock for intersect2

@VincentLanglet
Copy link
Contributor

I just encountered this bug in a situation like https://psalm.dev/r/324fd96838

This shouldn't be too hard to fix, since it is working with the typeAlias last https://psalm.dev/r/89e0dffd1b

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/324fd96838
<?php

namespace SonataFoo;

/**
 * @phpstan-type SuperFoo = array{foo: int}
 */
class Foo {}


namespace SonataBar;

/**
 * @phpstan-import-type SuperFoo from \SonataFoo\Foo
 * @phpstan-type SuperBar = SuperFoo&array{bar: int}
 */
class Bar {}
Psalm output (using commit 59d3d2a):

ERROR: InvalidDocblock - 17:1 - Intersection types must be all objects, Psalm\Type\Atomic\TKeyedArray provided
https://psalm.dev/r/89e0dffd1b
<?php

namespace SonataFoo;

/**
 * @phpstan-type SuperFoo = array{foo: int}
 */
class Foo {}


namespace SonataBar;

/**
 * @phpstan-import-type SuperFoo from \SonataFoo\Foo
 * @phpstan-type SuperBar = array{bar: int}&SuperFoo
 */
class Bar {}
Psalm output (using commit 59d3d2a):

No issues!

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

No branches or pull requests

4 participants