pattern
Glob matching, prefix/suffix/contains, and tuple filtering.
import type {
MatchesGlob,
StartsAnyOf, EndsAnyOf, IncludesAnyOf,
FilterMatching,
} from '@gentleduck/ttest/pattern'
Glob semantics:
*— zero or more characters (greedy substring)?— exactly one character**— any depth, including across/
MatchesGlob
type MatchesGlob<S extends string, P extends string>
true if S matches glob P. ** triggers branchy backtracking — worst case is quadratic. Fine for tooling; avoid in hot generic constraints.
type a = MatchesGlob<'src/app.ts', 'src/*.ts'> // true
type b = MatchesGlob<'src/x/y.ts', 'src/*.ts'> // false
type c = MatchesGlob<'src/x/y.ts', 'src/**'> // true
type d = MatchesGlob<'index.ts', '?ndex.ts'> // true
StartsAnyOf
type StartsAnyOf<S extends string, Prefixes extends readonly string[]>
true if S starts with any of Prefixes.
type a = StartsAnyOf<'/api/users', ['/api', '/auth']> // true
EndsAnyOf
type EndsAnyOf<S extends string, Suffixes extends readonly string[]>
type a = EndsAnyOf<'logo.svg', ['.png', '.svg']> // true
IncludesAnyOf
type IncludesAnyOf<S extends string, Needles extends readonly string[]>
true if S contains any of the substrings.
type a = IncludesAnyOf<'hello world', ['foo', 'world']> // true
FilterMatching
type FilterMatching<T extends readonly string[], P extends string, Acc extends string[] = []>
Keep only the strings in T that match glob P.
type t = FilterMatching<['a.ts', 'b.js', 'c.ts'], '*.ts'> // ['a.ts', 'c.ts']