@@ -188,22 +188,65 @@ export type GaleKeys<T extends string> = T | GaleBuiltinKey;
188188/** Hook to be called inside a component to get the `m` function. See {@link gale} */
189189export type GaleHook < T extends string > = ( ) => GaleFn < T > ;
190190/** The `m` function that turns a style string into class names */
191- export type GaleFn < T extends string > = < K extends string > ( classes : GaleString < K , T > ) => string ;
191+ export type GaleFn < T extends string > = < K extends string > ( classes : GaleString < K , T , [ ] > ) => string ;
192192
193193/** Type-safe, space-separated style idents */
194- export type GaleString < K extends string , T extends string > = K extends T
195- ? K
196- : K extends `${infer U } ${infer N } `
197- ? U extends T
198- ? GaleString < N , T > extends N
199- ? `${U } ${GaleString < N , T > } `
200- : Prettify < GaleString < N , T > & { After : U } >
201- : { InvalidStyleIdent : U }
202- : { InvalidStyleIdent : K } ;
194+ export type GaleString < K extends string , T extends string , S extends string [ ] > = K extends S [ number ]
195+ ? { DuplicateStyleIdent : K }
196+ : K extends T
197+ ? K
198+ : K extends `${infer U } ${infer N } `
199+ ? U extends S [ number ]
200+ ? { DuplicateStyleIdent : U }
201+ : U extends T
202+ ? GaleString < N , T , [ ...S , U ] > extends N
203+ ? `${U } ${GaleString < N , T , [ ...S , U ] > } `
204+ : Prettify < ExtendAfter < GaleString < N , T , [ ...S , U ] > , U > >
205+ : { InvalidStyleIdent : U }
206+ : { InvalidStyleIdent : K } ;
207+ type ExtendAfter < T , U > = T extends { After : string } ? T : T & { After : U } ;
203208type Prettify < T > = {
204209 [ K in keyof T ] : T [ K ] ;
205210} & { } ;
206211
212+ if ( import . meta. vitest ) {
213+ const { test, expectTypeOf } = import . meta. vitest ;
214+ type Validate < K extends string > = GaleString < K , "foo" | "bar" | `long-${number } `, [ ] > ;
215+ test ( "GaleString" , ( ) => {
216+ expectTypeOf < Validate < "bar" > > ( ) . toExtend < string > ( ) ;
217+ expectTypeOf < Validate < "foo" > > ( ) . toExtend < string > ( ) ;
218+ expectTypeOf < Validate < "biz" > > ( ) . toEqualTypeOf < { InvalidStyleIdent : "biz" } > ( ) ;
219+
220+ expectTypeOf < Validate < "foo bar" > > ( ) . toExtend < string > ( ) ;
221+ expectTypeOf < Validate < "foo foo" > > ( ) . toEqualTypeOf < {
222+ DuplicateStyleIdent : "foo" ;
223+ After : "foo" ;
224+ } > ( ) ;
225+ expectTypeOf < Validate < "foo biz" > > ( ) . toEqualTypeOf < {
226+ InvalidStyleIdent : "biz" ;
227+ After : "foo" ;
228+ } > ( ) ;
229+ expectTypeOf < Validate < "biz foo" > > ( ) . toEqualTypeOf < { InvalidStyleIdent : "biz" } > ( ) ;
230+ expectTypeOf < Validate < "foo bar biz" > > ( ) . toEqualTypeOf < {
231+ InvalidStyleIdent : "biz" ;
232+ After : "bar" ;
233+ } > ( ) ;
234+ expectTypeOf < Validate < "foo biz biz" > > ( ) . toEqualTypeOf < {
235+ InvalidStyleIdent : "biz" ;
236+ After : "foo" ;
237+ } > ( ) ;
238+ expectTypeOf < Validate < "foo biz bar" > > ( ) . toEqualTypeOf < {
239+ InvalidStyleIdent : "biz" ;
240+ After : "foo" ;
241+ } > ( ) ;
242+ expectTypeOf < Validate < "biz biz bar" > > ( ) . toEqualTypeOf < { InvalidStyleIdent : "biz" } > ( ) ;
243+ expectTypeOf < Validate < "biz biz" > > ( ) . toEqualTypeOf < { InvalidStyleIdent : "biz" } > ( ) ;
244+ expectTypeOf <
245+ Validate < "foo bar long-1 long-2 long-3 long-4 long-5 big long-6 long-7 long-8 long-9" >
246+ > ( ) . toEqualTypeOf < { InvalidStyleIdent : "big" ; After : "long-5" } > ( ) ;
247+ } ) ;
248+ }
249+
207250/** Built-in styles for {@link gale} */
208251export const GALE_BUILTIN_STYLES = {
209252 "wh-100v" : {
0 commit comments