vimeo / psalm

A static analysis tool for finding errors in PHP applications
https://psalm.dev
MIT License
5.58k stars 664 forks source link

Problem with literals #4564

Open thomasvargiu opened 4 years ago

thomasvargiu commented 4 years ago

I'm trying to write stubs for a liibrary and I have some problems with literal strings. I just need to compare the type and not the value.

How can I do something like that?

This is a simple example where I would like that the callable should return the same type of the input param, but not the same. There is a way to do it? Maybe would be nice some magic type like non-literal<A>?

/**
 * @template A
 * @param A $a
 * @return callable(A): A
 */
function foo($a): callable
{
    return fn ($b) => $b;
}

foo('a')('b');

https://psalm.dev/r/915c2c72eb

psalm-github-bot[bot] commented 4 years ago

I found these snippets:

https://psalm.dev/r/915c2c72eb ```php $b; } foo('a')('b'); ``` ``` Psalm output (using commit 4e8fb9c): INFO: UnusedParam - 8:14 - Param $a is never referenced in this method ERROR: InvalidArgument - 13:10 - Argument 1 expects string(a), string(b) provided ```
muglug commented 4 years ago

Ok, this might be grounds for a slightly breaking change (that would bring Psalm more in line with PHPStan).

I like TypeScript's behaviour, where specifying:

function identity<T extends string|object|boolean|void|undefined|number|null>(t: T) : T {
    return t;
}

let a = identity('a');

if (a === 'b') {} // a type error

vs

function identity<T /** implicitly extends any */>(t: T) : T {
    return t;
}

let a = identity('a');

if (a === 'b') {} // allowed

So in Psalm

/**
 * @template A as string
 * @param A $a
 * @return callable(A): A
 */
function foo($a): callable
{
    return fn ($b) => $b;
}

foo('a')('b'); // forbidden
/**
 * @template A
 * @param A $a
 * @return callable(A): A
 */
function foo($a): callable
{
    return fn ($b) => $b;
}

foo('a')('b'); // allowed

cc @ondrejmirtes @TysonAndre