# Templating ## `@template` The `@template` tag allows classes and functions to implement type parameter-like functionality found in many other languages. While `@template` tag order matters (i.e. for key-value pair extending), names don't matter outside the scope of the class or function in which they're declared. As a very simple example, this function returns whatever is passed in: ```php /** * @template T * @psalm-param T $t * @return T */ function mirror($t) { return $t; } $a = 5; $b = mirror(5); // Psalm knows the result is an int $c = "foo"; $d = mirror($c); // Psalm knows the result is string ``` Psalm also uses `@template` annotations in its stubbed versions of PHP array functions e.g. ```php /** * Takes one array with keys and another with values and combines them * * @template TKey * @template TValue * * @param array $arr * @param array $arr2 * @return array */ function array_combine(array $arr, array $arr2) {} ``` ## `@param class-string` Psalm also allows you to parameterise class types ```php /** * @template T * @psalm-param class-string $class * @return T */ function instantiator(string $class) { return new $class(); } class Foo {} $a = instantiator(Foo::class); // Psalm knows the result is an object of type Foo ``` ## Template inheritance Psalm allows you to extend templated classes with `@extends`/`@template-extends`: ```php /** * @template T */ class ParentClass {} /** * @extends ParentClass */ class ChildClass extends ParentClass {} ``` similarly you can implement interfaces with `@implements`/`@template-implements` ```php /** * @template T */ interface IFoo {} /** * @implements IFoo */ class Foo implements IFoo {} ``` and import traits with `@use`/`@template-use` ```php /** * @template T */ trait MyTrait {} class Foo { /** * @use MyTrait */ use MyTrait; } ``` You can also extend one templated class with another, e.g. ```php /** * @template T1 */ class ParentClass {} /** * @template T2 * @extends ParentClass */ class ChildClass extends ParentClass {} ``` ## Template constraints You can use `@template of ` to restrict input. For example, to restrict to a given class you can use ```php class Foo {} class FooChild extends Foo {} /** * @template T of Foo * @psalm-param T $class * @return array */ function makeArray($t) { return [$t]; } $a = makeArray(new Foo()); // typed as array $b = makeArray(new FooChild()); // typed as array $c = makeArray(new stdClass()); // type error ``` Templated types aren't limited to key-value pairs, and you can re-use templates across multiple arguments of a template-supporting type: ```php /** * @template T0 as array-key * * @template-implements IteratorAggregate */ abstract class Foo implements IteratorAggregate { /** * @var int */ protected $rand_min; /** * @var int */ protected $rand_max; public function __construct(int $rand_min, int $rand_max) { $this->rand_min = $rand_min; $this->rand_max = $rand_max; } /** * @return Generator */ public function getIterator() : Generator { $j = random_int($this->rand_min, $this->rand_max); for($i = $this->rand_min; $i <= $j; $i += 1) { yield $this->getFuzzyType($i) => $i ** $i; } return $this->getFuzzyType($j); } /** * @return T0 */ abstract protected function getFuzzyType(int $i); } /** * @template-extends Foo */ class Bar extends Foo { protected function getFuzzyType(int $i) : int { return $i; } } /** * @template-extends Foo */ class Baz extends Foo { protected function getFuzzyType(int $i) : string { return static::class . '[' . $i . ']'; } } ``` ## Builtin templated classes and interfaces Psalm has support for a number of builtin classes and interfaces that you can extend/implement in your own code. - `interface Traversable` - `interface ArrayAccess` - `interface IteratorAggregate extends Traversable` - `interface Iterator extends Traversable` - `interface SeekableIterator extends Iterator` - `class Generator extends Traversable` - `class ArrayObject implements IteratorAggregate, ArrayAccess` - `class ArrayIterator implements SeekableIterator, ArrayAccess` - `class DOMNodeList implements Traversable` - `class SplDoublyLinkedList implements Iterator, ArrayAccess` - `class SplQueue extends SplDoublyLinkedList`