@@ -3,7 +3,7 @@ import type { Predicate, PredicateType } from "../type.ts";
33import { isArray } from "./array.ts" ;
44
55/**
6- * Return a type predicate function that returns `true` if the type of `x` is `TupleOf<T>` or `TupleOf<T, R>` .
6+ * Return a type predicate function that returns `true` if the type of `x` is `TupleOf<T>`.
77 *
88 * Use {@linkcode isUniformTupleOf} to check if the type of `x` is a tuple of uniform types.
99 *
@@ -19,7 +19,7 @@ import { isArray } from "./array.ts";
1919 * }
2020 * ```
2121 *
22- * With `predRest` to represent rest elements:
22+ * With `predRest` to represent rest elements or leading rest elements :
2323 *
2424 * ```ts
2525 * import { is } from "@core/unknownutil";
@@ -32,6 +32,30 @@ import { isArray } from "./array.ts";
3232 * if (isMyType(a)) {
3333 * const _: [number, string, boolean, ...number[]] = a;
3434 * }
35+ *
36+ * const isMyTypeLeadingRest = is.TupleOf(
37+ * is.ArrayOf(is.Number),
38+ * [is.Number, is.String, is.Boolean],
39+ * );
40+ * if (isMyTypeLeadingRest(a)) {
41+ * const _: [...number[], number, string, boolean] = a;
42+ * }
43+ * ```
44+ *
45+ * With `predRest` and `predTrail` to represent middle rest elements:
46+ *
47+ * ```ts
48+ * import { is } from "@core/unknownutil";
49+ *
50+ * const isMyType = is.TupleOf(
51+ * [is.Number, is.String, is.Boolean],
52+ * is.ArrayOf(is.Number),
53+ * [is.Number, is.String, is.Boolean],
54+ * );
55+ * const a: unknown = [0, "a", true, 0, 1, 2, 0, "a", true];
56+ * if (isMyType(a)) {
57+ * const _: [number, string, boolean, ...number[], number, string, boolean] = a;
58+ * }
3559 * ```
3660 *
3761 * Depending on the version of TypeScript and how values are provided, it may be necessary to add `as const` to the array
@@ -62,13 +86,57 @@ export function isTupleOf<
6286 predRest : R ,
6387) : Predicate < [ ...TupleOf < T > , ...PredicateType < R > ] > ;
6488
89+ export function isTupleOf <
90+ R extends Predicate < unknown [ ] > ,
91+ T extends readonly [ Predicate < unknown > , ...Predicate < unknown > [ ] ] ,
92+ > (
93+ predRest : R ,
94+ predTup : T ,
95+ ) : Predicate < [ ...PredicateType < R > , ...TupleOf < T > ] > ;
96+
6597export function isTupleOf <
6698 T extends readonly [ Predicate < unknown > , ...Predicate < unknown > [ ] ] ,
6799 R extends Predicate < unknown [ ] > ,
100+ L extends readonly [ Predicate < unknown > , ...Predicate < unknown > [ ] ] ,
68101> (
69102 predTup : T ,
70- predRest ?: R ,
71- ) : Predicate < TupleOf < T > | [ ...TupleOf < T > , ...PredicateType < R > ] > {
103+ predRest : R ,
104+ predTrail : L ,
105+ ) : Predicate < [ ...TupleOf < T > , ...PredicateType < R > , ...TupleOf < L > ] > ;
106+
107+ export function isTupleOf <
108+ T extends readonly [ Predicate < unknown > , ...Predicate < unknown > [ ] ] ,
109+ R extends Predicate < unknown [ ] > ,
110+ L extends readonly [ Predicate < unknown > , ...Predicate < unknown > [ ] ] ,
111+ > (
112+ predTupOrRest : T | R ,
113+ predRestOrTup ?: R | T ,
114+ predTrail ?: L ,
115+ ) : Predicate <
116+ | TupleOf < T >
117+ | [ ...TupleOf < T > , ...PredicateType < R > ]
118+ | [ ...PredicateType < R > , ...TupleOf < T > ]
119+ | [ ...TupleOf < T > , ...PredicateType < R > , ...TupleOf < L > ]
120+ > {
121+ if ( typeof predTupOrRest === "function" ) {
122+ const predRest = predTupOrRest as R ;
123+ const predTup = predRestOrTup as T ;
124+ return rewriteName (
125+ ( x : unknown ) : x is [ ...PredicateType < R > , ...TupleOf < T > ] => {
126+ if ( ! isArray ( x ) || x . length < predTup . length ) {
127+ return false ;
128+ }
129+ const offset = x . length - predTup . length ;
130+ return predTup . every ( ( pred , i ) => pred ( x [ offset + i ] ) ) &&
131+ predRest ( x . slice ( 0 , offset ) ) ;
132+ } ,
133+ "isTupleOf" ,
134+ predRest ,
135+ predTup ,
136+ ) ;
137+ }
138+ const predTup = predTupOrRest as T ;
139+ const predRest = predRestOrTup as R ;
72140 if ( ! predRest ) {
73141 return rewriteName (
74142 ( x : unknown ) : x is TupleOf < T > => {
@@ -80,19 +148,36 @@ export function isTupleOf<
80148 "isTupleOf" ,
81149 predTup ,
82150 ) ;
83- } else {
151+ } else if ( ! predTrail ) {
84152 return rewriteName (
85153 ( x : unknown ) : x is [ ...TupleOf < T > , ...PredicateType < R > ] => {
86154 if ( ! isArray ( x ) || x . length < predTup . length ) {
87155 return false ;
88156 }
89- const head = x . slice ( 0 , predTup . length ) ;
90- const tail = x . slice ( predTup . length ) ;
91- return predTup . every ( ( pred , i ) => pred ( head [ i ] ) ) && predRest ( tail ) ;
157+ return predTup . every ( ( pred , i ) => pred ( x [ i ] ) ) &&
158+ predRest ( x . slice ( predTup . length ) ) ;
159+ } ,
160+ "isTupleOf" ,
161+ predTup ,
162+ predRest ,
163+ ) ;
164+ } else {
165+ return rewriteName (
166+ (
167+ x : unknown ,
168+ ) : x is [ ...TupleOf < T > , ...PredicateType < R > , ...TupleOf < L > ] => {
169+ if ( ! isArray ( x ) || x . length < ( predTup . length + predTrail . length ) ) {
170+ return false ;
171+ }
172+ const offset = x . length - predTrail . length ;
173+ return predTup . every ( ( pred , i ) => pred ( x [ i ] ) ) &&
174+ predTrail . every ( ( pred , i ) => pred ( x [ offset + i ] ) ) &&
175+ predRest ( x . slice ( predTup . length , offset ) ) ;
92176 } ,
93177 "isTupleOf" ,
94178 predTup ,
95179 predRest ,
180+ predTrail ,
96181 ) ;
97182 }
98183}
0 commit comments