-
Notifications
You must be signed in to change notification settings - Fork 663
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
Templates & late static binding #8142
Comments
I found these snippets: https://psalm.dev/r/10d35322cc<?php
$ARR = [
'A' => 'B'
];
/** @psalm-trace $m */
$m = new Map($ARR);
$m->every(fn(KeyValuePair $entry) : bool => $ARR[$entry->key()] === $entry->value());
/**
* @template TValue
*/
abstract class Container
{
/** @var array<int, TValue> */
protected array $entries;
// TValue will depend on the implementation of fromIterable()
final function __construct(iterable $entries = [])
{
$this->entries = static::fromIterable($entries);
}
/**
* @template TStaticKey
* @template TStaticValue
* @param iterable<TStaticKey, TStaticValue> $iterable
* @return array<int, mixed> // we don't know how TStaticKey and TStaticValue are transformed, hence mixed
*/
abstract static protected function fromIterable(iterable $iterable) : array;
}
/**
* @template TEntryKey
* @template TEntryValue
* @extends Container<KeyValuePair<TEntryKey, TEntryValue>>
* @method __construct(iterable<TEntryKey, TEntryValue> $entries = [])
*/
class Map extends Container
{
/**
* @template TStaticKey
* @template TStaticValue
* @param iterable<TStaticKey, TStaticValue> $iterable
* @return array<int, KeyValuePair<TStaticKey, TStaticValue>>
*/
final static protected function fromIterable(iterable $iterable) : array
{
$entries = [];
foreach($iterable as $k => $v) $entries[] = new KeyValuePair($k, $v);
return $entries;
}
/** @param \Closure(KeyValuePair<TEntryKey, TEntryValue>) : bool $process */
final public function every(\Closure $process) : bool
{
foreach($this->entries as $entry) if(!$process($entry)) return false;
return true;
}
}
/** @template TKey
* @template TValue */
final class KeyValuePair
{
/** @var TKey */ private $key;
/** @var TValue */ private $value;
/** @return TKey */ public function key() { return $this->key; }
/** @return TValue */ public function &value() { return $this->value; }
/** @param TKey $key
* @param TValue $value */
public function __construct($key, $value) { $this->key = $key; $this->value = $value; }
}
|
I found these snippets: https://psalm.dev/r/f115294a4f<?php
$ARR = [
'A' => 'B'
];
/** @psalm-trace $m */
$m = new Map($ARR);
$m->every(fn(KeyValuePair $entry) : bool => $ARR[$entry->key()] === $entry->value());
/**
* @template TValue
* @template TStaticKey
* @template TStaticValue
*/
abstract class Container
{
/** @var array<int, TValue> */
protected array $entries;
/**
* @param iterable<TStaticKey, TStaticValue> $entries
*/
final function __construct(iterable $entries = [])
{
$this->entries = $this->fromIterable($entries);
}
/**
* @param iterable<TStaticKey, TStaticValue> $iterable
* @return array<int, TValue>
*/
abstract protected function fromIterable(iterable $iterable) : array;
}
/**
* @template TEntryKey
* @template TEntryValue
* @extends Container<KeyValuePair<TEntryKey, TEntryValue>, TEntryKey, TEntryValue>
*/
class Map extends Container
{
/**
* @param iterable<TEntryKey, TEntryValue> $iterable
* @return array<int, KeyValuePair<TEntryKey, TEntryValue>>
*/
final protected function fromIterable(iterable $iterable) : array
{
$entries = [];
foreach($iterable as $k => $v) $entries[] = new KeyValuePair($k, $v);
return $entries;
}
/** @param \Closure(KeyValuePair<TEntryKey, TEntryValue>) : bool $process */
final public function every(\Closure $process) : bool
{
foreach($this->entries as $entry) if(!$process($entry)) return false;
return true;
}
}
/** @template TKey
* @template TValue */
final class KeyValuePair
{
/** @var TKey */ private $key;
/** @var TValue */ private $value;
/** @return TKey */ public function key() { return $this->key; }
/** @return TValue */ public function &value() { return $this->value; }
/** @param TKey $key
* @param TValue $value */
public function __construct($key, $value) { $this->key = $key; $this->value = $value; }
}
|
Thanks for your suggestion, @AndrolGenhald btw, by adding an explicit ctor to But for that, I had to remove the |
I found these snippets: https://psalm.dev/r/2419c49458<?php
$ARR = [
'A' => 'B'
];
/** @psalm-trace $m */
$m = new Map($ARR);
$m->every(fn(KeyValuePair $entry) : bool => $ARR[$entry->key()] === $entry->value());
/**
* @template TValue
* @psalm-consistent-constructor
*/
abstract class Container
{
/** @var array<int, TValue> */
protected array $entries;
// TValue will depend on the implementation of fromIterable()
/*final*/function __construct(iterable $entries = [])
{
$this->entries = static::fromIterable($entries);
}
/**
* @template TStaticKey
* @template TStaticValue
* @param iterable<TStaticKey, TStaticValue> $iterable
* @return array<int, mixed> // we don't know how TStaticKey and TStaticValue are transformed, hence mixed
*/
abstract static protected function fromIterable(iterable $iterable) : array;
}
/**
* @template TEntryKey
* @template TEntryValue
* @extends Container<KeyValuePair<TEntryKey, TEntryValue>>
*/
class Map extends Container
{
/** @param iterable<TEntryKey, TEntryValue> $entries */
final function __construct(iterable $entries = []) { parent::__construct($entries); }
/**
* @template TStaticKey
* @template TStaticValue
* @param iterable<TStaticKey, TStaticValue> $iterable
* @return array<int, KeyValuePair<TStaticKey, TStaticValue>>
*/
final static protected function fromIterable(iterable $iterable) : array
{
$entries = [];
foreach($iterable as $k => $v) $entries[] = new KeyValuePair($k, $v);
return $entries;
}
/** @param \Closure(KeyValuePair<TEntryKey, TEntryValue>) : bool $process */
final public function every(\Closure $process) : bool
{
foreach($this->entries as $entry) if(!$process($entry)) return false;
return true;
}
}
/** @template TKey
* @template TValue */
final class KeyValuePair
{
/** @var TKey */ private $key;
/** @var TValue */ private $value;
/** @return TKey */ public function key() { return $this->key; }
/** @return TValue */ public function &value() { return $this->value; }
/** @param TKey $key
* @param TValue $value */
public function __construct($key, $value) { $this->key = $key; $this->value = $value; }
}
|
In that case you're overriding the constructor so that you can use the |
I found these snippets: https://psalm.dev/r/2419c49458<?php
$ARR = [
'A' => 'B'
];
/** @psalm-trace $m */
$m = new Map($ARR);
$m->every(fn(KeyValuePair $entry) : bool => $ARR[$entry->key()] === $entry->value());
/**
* @template TValue
* @psalm-consistent-constructor
*/
abstract class Container
{
/** @var array<int, TValue> */
protected array $entries;
// TValue will depend on the implementation of fromIterable()
/*final*/function __construct(iterable $entries = [])
{
$this->entries = static::fromIterable($entries);
}
/**
* @template TStaticKey
* @template TStaticValue
* @param iterable<TStaticKey, TStaticValue> $iterable
* @return array<int, mixed> // we don't know how TStaticKey and TStaticValue are transformed, hence mixed
*/
abstract static protected function fromIterable(iterable $iterable) : array;
}
/**
* @template TEntryKey
* @template TEntryValue
* @extends Container<KeyValuePair<TEntryKey, TEntryValue>>
*/
class Map extends Container
{
/** @param iterable<TEntryKey, TEntryValue> $entries */
final function __construct(iterable $entries = []) { parent::__construct($entries); }
/**
* @template TStaticKey
* @template TStaticValue
* @param iterable<TStaticKey, TStaticValue> $iterable
* @return array<int, KeyValuePair<TStaticKey, TStaticValue>>
*/
final static protected function fromIterable(iterable $iterable) : array
{
$entries = [];
foreach($iterable as $k => $v) $entries[] = new KeyValuePair($k, $v);
return $entries;
}
/** @param \Closure(KeyValuePair<TEntryKey, TEntryValue>) : bool $process */
final public function every(\Closure $process) : bool
{
foreach($this->entries as $entry) if(!$process($entry)) return false;
return true;
}
}
/** @template TKey
* @template TValue */
final class KeyValuePair
{
/** @var TKey */ private $key;
/** @var TValue */ private $value;
/** @return TKey */ public function key() { return $this->key; }
/** @return TValue */ public function &value() { return $this->value; }
/** @param TKey $key
* @param TValue $value */
public function __construct($key, $value) { $this->key = $key; $this->value = $value; }
}
|
Please consider this example: https://psalm.dev/r/10d35322cc
Container<T>
has a final ctor, which callsstatic::fromIterable()
.Map
extendsContainer
.Map::fromIterable()
decides theTValue
that goes into the Container.The problem here is that Psalm fails to detect the type of
Map<TKey, TValue>
and concludes it's a generic
Map<mixed, mixed>
. (see Trace)@ line 37, I try to override the definition of
Map
's ctor via @method, but to no avail..The text was updated successfully, but these errors were encountered: