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

Problem with literals #4564

Open
thomasvargiu opened this issue Nov 16, 2020 · 2 comments
Open

Problem with literals #4564

thomasvargiu opened this issue Nov 16, 2020 · 2 comments

Comments

@thomasvargiu
Copy link
Contributor

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
Copy link

I found these snippets:

https://psalm.dev/r/915c2c72eb
<?php

/**
 * @template A
 * @param A $a
 * @return callable(A): A
 */
function foo($a): callable
{
    return fn ($b) => $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
Copy link
Collaborator

muglug commented Nov 16, 2020

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

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

Successfully merging a pull request may close this issue.

3 participants