Open simPod opened 3 years ago
I found these snippets:
This use-case is already covered by conditional types: https://psalm.dev/r/2cf8b73f59
I found these snippets:
Ah well in that case I guess it's solved. Thanks!
This is still a bit wonky if condition gets more complicated (one more param appears).
Also, I'm either doing something wrong or it's not fully supported?
I found these snippets:
Promise example (currently impossible to type)
/** @template T */
interface Promise
{
/**
* @template TResult1
* @template TResult2
* @template-default TResult1=T
* @template-default TResult2=never
*
* @param (callable(T): TResult1)|null $a
* @param (callable(mixed): TResult2)|null $b
*
* @return
*/
public function then(callable|null $a = null, callable|null $b = null) : Promise;
}
class C {
private callable $aCall = fn(int $a): string => (string) $a;
private callable $bCall = fn(bool $b): bool => $b;
/**
* @param Promise<int> $promise
*
* @return Promise<int>
*/
public function nulls(Promise $promise) : Promise
{
return $promise->then(null, null);
}
/**
* @param Promise<int> $promise
*
* @return Promise<string|bool>
*/
public function a_b(Promise $promise) : Promise
{
return $promise->then($this->aCall, $this->bCall);
}
/**
* @param Promise<int> $promise
*
* @return Promise<string>
*/
function a(Promise $promise) : Promise
{
return $promise->then($this->aCall, null);
}
/**
* @param Promise<int> $promise
*
* @return Promise<bool>
*/
function b(Promise $promise) : Promise
{
return $promise->then(null, $this->bCall);
}
}
Conditional types are not a thing for this. It's crazy complicated to write something like that using them. I guess that's also the reason why it does not work in psalm now.
/**
* @template T
*/
interface Promise
{
/**
* @template TResult1
* @template TResult2
*
* @param (callable(T): TResult1)|null $a
* @param (callable(mixed): TResult2)|null $b
*
* @return (
* $a is null
* ? (
* $b is null
* ? Promise<T>
* : Promise<TResult2>
* )
* : (
* $b is null
* ? Promise<TResult1>
* : Promise<TResult1|TResult2>
* )
* )
*/
public function then(callable|null $a = null, callable|null $b = null) : Promise;
}
This has just been added to PHPStan with the following syntax:
<?php
/**
* @template T
* @template U = true
*/
class Foo
{
}
If template is impossible to resolve, eg. because the type is
T|null
, it should be possible to specify a default typeConsider this: If a value passed for
$a
isnull
, it's impossible to resolveTResult
's type. Then default type should be used. Currently, the default type everywhere ismixed
instead.It should be possible to say that
TResult
default isT
. Therefore, when callingwork(null)
onI<string>
, the return type would bestring
.https://psalm.dev/r/254659fcc2
This feature is already present in other languages like typescript or java