Skip to content

Casting down iterable doesn't give the expected type #1997

@bugreportuser

Description

@bugreportuser

iterable can't be cast to array, which should be valid:
https://www.php.net/manual/en/function.is-iterable.php

When iterable is cast to Traversable, it loses template types.

Example (https://psalm.dev/r/426c998c6e):

<?php
/**
 * @param array<int> $x
 * @psalm-suppress UnusedParam
 */
function takesArray (array $x): void {}

/** @var iterable<int> */
$x = null;
assert(is_array($x));
takesArray($x);
if (is_array($x)) {
    takesArray($x);
}

/**
 * @param Traversable<int> $x
 * @psalm-suppress UnusedParam
 */
function takesTraversable (Traversable $x): void {}

/** @var iterable<int> */
$x = null;
assert($x instanceof Traversable);
takesTraversable($x);

Example output:

Psalm output (using commit 3f78082):

ERROR: InvalidArgument - 11:12 - Argument 1 of takesArray expects array<array-key, int>, iterable<array-key, int> provided

ERROR: DocblockTypeContradiction - 12:5 - Cannot resolve types for $x - docblock-defined type iterable<array-key, int> does not contain array<array-key, mixed>

ERROR: MixedArgumentTypeCoercion - 13:16 - Argument 1 of takesArray expects array<array-key, int>, parent type array<array-key, mixed> provided

ERROR: MixedArgumentTypeCoercion - 25:18 - Argument 1 of takesTraversable expects Traversable<mixed, int>, parent type Traversable provided

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions