@@ -56,6 +56,70 @@ public function find(array $request): void
5656 }
5757}
5858
59+ // -------------------------------------------------------
60+ // Two template params
61+ // -------------------------------------------------------
62+
63+ /**
64+ * @phpstan-type Pair<TFirst, TSecond> array{first: TFirst, second: TSecond}
65+ */
66+ class PairHolder
67+ {
68+ /**
69+ * @param Pair<string, int> $pair
70+ */
71+ public function check (array $ pair ): void
72+ {
73+ assertType ('string ' , $ pair ['first ' ]);
74+ assertType ('int ' , $ pair ['second ' ]);
75+ }
76+ }
77+
78+ // -------------------------------------------------------
79+ // @return of generic alias with bound constraint
80+ // -------------------------------------------------------
81+
82+ /**
83+ * @phpstan-type Range<T of int|float> array{min: T, max: T}
84+ */
85+ class RangeHolder
86+ {
87+ /**
88+ * @param Range<int> $r
89+ * @return Range<float>
90+ */
91+ public function convert (array $ r ): array
92+ {
93+ assertType ('int ' , $ r ['min ' ]);
94+ assertType ('int ' , $ r ['max ' ]);
95+ $ result = ['min ' => (float ) $ r ['min ' ], 'max ' => (float ) $ r ['max ' ]];
96+ assertType ('array{min: float, max: float} ' , $ result );
97+ return $ result ;
98+ }
99+ }
100+
101+ // -------------------------------------------------------
102+ // @var property annotation
103+ // -------------------------------------------------------
104+
105+ /**
106+ * @phpstan-type Config<TValue> array{key: string, value: TValue}
107+ */
108+ class Settings
109+ {
110+ /** @var Config<int> */
111+ public array $ timeout = ['key ' => 'timeout ' , 'value ' => 30 ];
112+
113+ /** @var Config<string> */
114+ public array $ name = ['key ' => 'name ' , 'value ' => 'default ' ];
115+
116+ public function check (): void
117+ {
118+ assertType ('int ' , $ this ->timeout ['value ' ]);
119+ assertType ('string ' , $ this ->name ['value ' ]);
120+ }
121+ }
122+
59123// -------------------------------------------------------
60124// Test with list<T>
61125// -------------------------------------------------------
@@ -75,6 +139,25 @@ public function check(array $result): void
75139 }
76140}
77141
142+ // -------------------------------------------------------
143+ // Nested generic alias (alias referencing another generic alias)
144+ // -------------------------------------------------------
145+
146+ /**
147+ * @phpstan-type Item<T> array{id: int, data: T}
148+ * @phpstan-type ItemList<T> list<Item<T>>
149+ */
150+ class ItemRepo
151+ {
152+ /**
153+ * @param ItemList<string> $items
154+ */
155+ public function process (array $ items ): void
156+ {
157+ assertType ('list<array{id: int, data: string}> ' , $ items );
158+ }
159+ }
160+
78161// -------------------------------------------------------
79162// Test with two template params
80163// -------------------------------------------------------
@@ -94,7 +177,7 @@ public function check(array $m): void
94177}
95178
96179// -------------------------------------------------------
97- // Test with default template param value
180+ // Default param: explicit arg vs bare usage (default applied)
98181// -------------------------------------------------------
99182
100183/**
@@ -103,21 +186,24 @@ public function check(array $m): void
103186class DefaultHolder
104187{
105188 /**
106- * @param WithDefault<int> $withInt
189+ * @param WithDefault<int> $explicit explicit arg overrides default
190+ * @param WithDefault $implicit bare usage – T defaults to string
107191 */
108- public function check (array $ withInt ): void
192+ public function check (array $ explicit , array $ implicit ): void
109193 {
110- assertType ('int ' , $ withInt ['value ' ]);
194+ assertType ('int ' , $ explicit ['value ' ]);
195+ assertType ('string ' , $ implicit ['value ' ]);
111196 }
112197}
113198
114199// -------------------------------------------------------
115- // Test @phpstan-import-type of a generic alias
200+ // @phpstan-import-type of a generic alias
116201// -------------------------------------------------------
117202
118203/**
119204 * @phpstan-import-type Map from MapHolder
120205 * @phpstan-import-type Paged from Repo
206+ * @phpstan-import-type Pair from PairHolder
121207 */
122208class ImportConsumer
123209{
@@ -137,6 +223,13 @@ public function pagedCheck(array $p): void
137223 assertType ('list<DateTime> ' , $ p ['items ' ]);
138224 assertType ('int ' , $ p ['total ' ]);
139225 }
140- }
141-
142226
227+ /**
228+ * @param Pair<int, bool> $p
229+ */
230+ public function pairCheck (array $ p ): void
231+ {
232+ assertType ('int ' , $ p ['first ' ]);
233+ assertType ('bool ' , $ p ['second ' ]);
234+ }
235+ }
0 commit comments