psalm
psalm copied to clipboard
array_walk callback 2nd param type inference
https://psalm.dev/r/93b224455b
array_walk($someArray, function ($value, $index) {
});
it seems to me that Psalm should be able to infer that $index is of typearray-key and not mixed?
I found these snippets:
https://psalm.dev/r/93b224455b
<?php
/**
* @param array<array-key, mixed> $array
*
* @return void
*/
function bob(array $array)
{
\array_walk($array, function ($val, $index) {
sprintf('index %s', $index);
});
}
Psalm output (using commit 4b2c698):
INFO: MixedArgument - 11:29 - Argument 2 of sprintf cannot be mixed, expecting float|int|object{__tostring()}|string
ERROR: UnusedFunctionCall - 11:9 - The call to sprintf is not used
INFO: MissingClosureParamType - 10:35 - Parameter $val has no provided type
INFO: MissingClosureParamType - 10:41 - Parameter $index has no provided type
The callback for array_walk doesn't have more specific types in psalm's callmap.
Ideally the annotation would be callable(&value-of<T>, key-of<T>, mixed=) or even better to create a separate \2 annotation where the optional 3rd arg is only included when arg is set and otherwise not.
For \1 with object would be like callable(&value-of<properties-of<T>>, key-of<properties-of<T>>, mixed=)
Same for array_walk_recursive I guess.
Related... psalm doesn't understand the assertions happening within array_walk
here, after array_walk, we should know that the array is array<array-key, int|string> vs array<array-key, mixed>
https://psalm.dev/r/e9cb9a41d0
I found these snippets:
https://psalm.dev/r/e9cb9a41d0
<?php
class Foo
{
/**
* Cast path to array
*
* @param array|string $path path
*
* @return array<array-key, string|int>
*
* @throws InvalidArgumentException
*
* @psalm-suppress RedundantConditionGivenDocblockType
* @psalm-suppress DocblockTypeContradiction
*/
private static function pathToArray($path)
{
if (\is_string($path)) {
// return \array_filter(\preg_split('#[\./]#', (string) $path), 'strlen');
return array();
}
if (\is_array($path) === false) {
throw new InvalidArgumentException('Path must be string or list of string|int');
}
// using array_walk vs foreach loop
\array_walk($path, static function ($val) {
if (\is_string($val) === false && \is_int($val) === false) {
throw new InvalidArgumentException('Path array must consist only of string|int');
}
});
return $path;
}
}
Psalm output (using commit cb0e6a1):
INFO: MissingClosureParamType - 27:45 - Parameter $val has no provided type
INFO: MixedReturnTypeCoercion - 32:16 - The type 'array<array-key, mixed>' is more general than the declared return type 'array<array-key, int|string>' for Foo::pathToArray
INFO: MixedReturnTypeCoercion - 10:16 - The declared return type 'array<array-key, int|string>' for Foo::pathToArray is more specific than the inferred return type 'array<array-key, mixed>'