@@ -80,3 +80,153 @@ public function getUser(): array
8080 return ['data ' => ['id ' => 1 , 'name ' => 'Alice ' ], 'status ' => 200 ];
8181 }
8282}
83+
84+ // ---------------------------------------------------------------------------
85+ // @return of generic alias
86+ // ---------------------------------------------------------------------------
87+
88+ /**
89+ * @phpstan-type Page<TItem of object> array{items: list<TItem>, total: int, page: int}
90+ */
91+ final class PagedRepo
92+ {
93+ /**
94+ * @return Page<\stdClass>
95+ */
96+ public function getPage (): array
97+ {
98+ dumpType ($ this ->getPage ()); // should show array{items: list<stdClass>, total: int, page: int}
99+ return ['items ' => [], 'total ' => 0 , 'page ' => 1 ];
100+ }
101+ }
102+
103+ // ---------------------------------------------------------------------------
104+ // @var property annotation
105+ // ---------------------------------------------------------------------------
106+
107+ /**
108+ * @phpstan-type Config<TValue> array{key: string, value: TValue}
109+ */
110+ final class Settings
111+ {
112+ /** @var Config<int> */
113+ public array $ timeout = ['key ' => 'timeout ' , 'value ' => 30 ];
114+
115+ /** @var Config<string> */
116+ public array $ name = ['key ' => 'name ' , 'value ' => 'default ' ];
117+
118+ public function check (): void
119+ {
120+ dumpType ($ this ->timeout ['value ' ]); // int
121+ dumpType ($ this ->name ['value ' ]); // string
122+ }
123+ }
124+
125+ // ---------------------------------------------------------------------------
126+ // Nested generic alias (alias referencing another generic alias with type args)
127+ // ---------------------------------------------------------------------------
128+
129+ /**
130+ * @phpstan-type Item<T> array{id: int, data: T}
131+ * @phpstan-type ItemList<T> list<Item<T>>
132+ */
133+ final class ItemRepo
134+ {
135+ /**
136+ * @param ItemList<string> $items
137+ */
138+ public function process (array $ items ): void
139+ {
140+ dumpType ($ items ); // list<array{id: int, data: string}>
141+ dumpType ($ items [0 ]['data ' ]); // string
142+ }
143+ }
144+
145+ // ---------------------------------------------------------------------------
146+ // @phpstan-import-type of a generic alias, then used with type args
147+ // ---------------------------------------------------------------------------
148+
149+ /**
150+ * @phpstan-import-type Pair from PairHolder
151+ */
152+ final class PairConsumer
153+ {
154+ /**
155+ * @param Pair<int, bool> $p
156+ */
157+ public function check (array $ p ): void
158+ {
159+ dumpType ($ p ['first ' ]); // int
160+ dumpType ($ p ['second ' ]); // bool
161+ }
162+ }
163+
164+ // ---------------------------------------------------------------------------
165+ // Default type arg — using alias WITHOUT args should be OK (default kicks in)
166+ // ---------------------------------------------------------------------------
167+
168+ /**
169+ * @phpstan-type WithDefault<T = string> array{value: T}
170+ */
171+ final class DefaultConsumer
172+ {
173+ /**
174+ * @param WithDefault<int> $explicit no error: type arg provided
175+ * @param WithDefault $implicit no error: T has a default
176+ */
177+ public function check (array $ explicit , array $ implicit ): void
178+ {
179+ dumpType ($ explicit ['value ' ]); // int
180+ dumpType ($ implicit ['value ' ]); // BUG: shows raw TemplateType instead of string — default not applied when alias used without args
181+ }
182+ }
183+
184+ // ---------------------------------------------------------------------------
185+ // Generic alias in a standalone function (not a class method)
186+ // ---------------------------------------------------------------------------
187+
188+ /**
189+ * @phpstan-type Range<T of int|float> array{min: T, max: T}
190+ */
191+ final class RangeHolder
192+ {
193+ /**
194+ * @param Range<int> $r
195+ * @return Range<float>
196+ */
197+ public function convert (array $ r ): array
198+ {
199+ dumpType ($ r ['min ' ]); // int
200+ return ['min ' => (float ) $ r ['min ' ], 'max ' => (float ) $ r ['max ' ]];
201+ }
202+ }
203+
204+ // ---------------------------------------------------------------------------
205+ // Too many type args — should error
206+ // ---------------------------------------------------------------------------
207+
208+ /**
209+ * @phpstan-type Single<T> array{value: T}
210+ */
211+ final class TooManyArgs
212+ {
213+ /**
214+ * @param Single<int, string> $x TODO: should error — Single takes 1 type arg, 2 given (not yet detected)
215+ */
216+ public function check (array $ x ): void {}
217+ }
218+
219+ // ---------------------------------------------------------------------------
220+ // Too few required type args (partial application of multi-param alias) — should error
221+ // ---------------------------------------------------------------------------
222+
223+ /**
224+ * @phpstan-type KeyValue<TKey of array-key, TValue> array{key: TKey, value: TValue}
225+ */
226+ final class TooFewArgs
227+ {
228+ /**
229+ * @param KeyValue<string> $x TODO: should error — KeyValue requires 2 type args (not yet detected)
230+ */
231+ public function check (array $ x ): void {}
232+ }
0 commit comments