@@ -103,23 +103,75 @@ export type ReadonlyTupleOf<T extends readonly Predicate<unknown>[]> = {
103103 * Note that `predTup` must be `readonly` (`as const`) to infer the type of `a` correctly.
104104 * TypeScript won't argues if `predTup` is not `readonly` because of its design limitation.
105105 * https://github.com/microsoft/TypeScript/issues/34274#issuecomment-541691353
106+ *
107+ * It can also be used to check the type of the rest of the tuple like:
108+ *
109+ * ```ts
110+ * import { is } from "https://deno.land/x/unknownutil@$MODULE_VERSION/mod.ts";
111+ *
112+ * const predTup = [is.Number, is.String, is.Boolean] as const;
113+ * const predElse = is.ArrayOf(is.Number);
114+ * const a: unknown = [0, "a", true, 0, 1, 2];
115+ * if (is.TupleOf(predTup, predElse)(a)) {
116+ * // a is narrowed to [number, string, boolean, ...number[]]
117+ * const _: [number, string, boolean, ...number[]] = a;
118+ * }
119+ * ```
106120 */
107- export function isTupleOf < T extends readonly Predicate < unknown > [ ] > (
121+ export function isTupleOf <
122+ T extends readonly Predicate < unknown > [ ] ,
123+ R extends TupleOf < T > ,
124+ > (
108125 predTup : T ,
109- ) : Predicate < TupleOf < T > > {
110- return Object . defineProperties (
111- ( x : unknown ) : x is TupleOf < T > => {
112- if ( ! isArray ( x ) || x . length !== predTup . length ) {
113- return false ;
114- }
115- return predTup . every ( ( pred , i ) => pred ( x [ i ] ) ) ;
116- } ,
117- {
118- name : {
119- get : ( ) => `isTupleOf(${ inspect ( predTup ) } )` ,
126+ ) : Predicate < R > ;
127+ export function isTupleOf <
128+ T extends readonly Predicate < unknown > [ ] ,
129+ E extends Predicate < unknown [ ] > ,
130+ R extends [ ...TupleOf < T > , ...PredicateType < E > ] ,
131+ > (
132+ predTup : T ,
133+ predElse : E ,
134+ ) : Predicate < R > ;
135+ export function isTupleOf <
136+ T extends readonly Predicate < unknown > [ ] ,
137+ E extends Predicate < unknown [ ] > ,
138+ R1 extends TupleOf < T > ,
139+ R2 extends [ ...TupleOf < T > , ...PredicateType < E > ] ,
140+ > (
141+ predTup : T ,
142+ predElse ?: E ,
143+ ) : Predicate < R1 | R2 > {
144+ if ( ! predElse ) {
145+ return Object . defineProperties (
146+ ( x : unknown ) : x is R1 => {
147+ if ( ! isArray ( x ) || x . length !== predTup . length ) {
148+ return false ;
149+ }
150+ return predTup . every ( ( pred , i ) => pred ( x [ i ] ) ) ;
120151 } ,
121- } ,
122- ) ;
152+ {
153+ name : {
154+ get : ( ) => `isTupleOf(${ inspect ( predTup ) } )` ,
155+ } ,
156+ } ,
157+ ) ;
158+ } else {
159+ return Object . defineProperties (
160+ ( x : unknown ) : x is R2 => {
161+ if ( ! isArray ( x ) || x . length < predTup . length ) {
162+ return false ;
163+ }
164+ const head = x . slice ( 0 , predTup . length ) ;
165+ const tail = x . slice ( predTup . length ) ;
166+ return predTup . every ( ( pred , i ) => pred ( head [ i ] ) ) && predElse ( tail ) ;
167+ } ,
168+ {
169+ name : {
170+ get : ( ) => `isTupleOf(${ inspect ( predTup ) } , ${ inspect ( predElse ) } )` ,
171+ } ,
172+ } ,
173+ ) ;
174+ }
123175}
124176
125177/**
@@ -139,18 +191,63 @@ export function isTupleOf<T extends readonly Predicate<unknown>[]>(
139191 * Note that `predTup` must be `readonly` (`as const`) to infer the type of `a` correctly.
140192 * TypeScript won't argues if `predTup` is not `readonly` because of its design limitation.
141193 * https://github.com/microsoft/TypeScript/issues/34274#issuecomment-541691353
194+ *
195+ * It can also be used to check the type of the rest of the tuple like:
196+ *
197+ * ```ts
198+ * import { is } from "https://deno.land/x/unknownutil@$MODULE_VERSION/mod.ts";
199+ *
200+ * const predTup = [is.Number, is.String, is.Boolean] as const;
201+ * const predElse = is.ArrayOf(is.Number);
202+ * const a: unknown = [0, "a", true, 0, 1, 2];
203+ * if (is.ReadonlyTupleOf(predTup, predElse)(a)) {
204+ * // a is narrowed to readonly [number, string, boolean, ...number[]]
205+ * const _: readonly [number, string, boolean, ...number[]] = a;
206+ * }
142207 */
143- export function isReadonlyTupleOf < T extends readonly Predicate < unknown > [ ] > (
208+ export function isReadonlyTupleOf <
209+ T extends readonly Predicate < unknown > [ ] ,
210+ R extends ReadonlyTupleOf < T > ,
211+ > (
144212 predTup : T ,
145- ) : Predicate < ReadonlyTupleOf < T > > {
146- return Object . defineProperties (
147- isTupleOf ( predTup ) as Predicate < ReadonlyTupleOf < T > > ,
148- {
149- name : {
150- get : ( ) => `isReadonlyTupleOf(${ inspect ( predTup ) } )` ,
213+ ) : Predicate < R > ;
214+ export function isReadonlyTupleOf <
215+ T extends readonly Predicate < unknown > [ ] ,
216+ E extends Predicate < unknown [ ] > ,
217+ R extends readonly [ ...ReadonlyTupleOf < T > , ...PredicateType < E > ] ,
218+ > (
219+ predTup : T ,
220+ predElse : E ,
221+ ) : Predicate < R > ;
222+ export function isReadonlyTupleOf <
223+ T extends readonly Predicate < unknown > [ ] ,
224+ E extends Predicate < unknown [ ] > ,
225+ R1 extends ReadonlyTupleOf < T > ,
226+ R2 extends readonly [ ...ReadonlyTupleOf < T > , ...PredicateType < E > ] ,
227+ > (
228+ predTup : T ,
229+ predElse ?: E ,
230+ ) : Predicate < R1 | R2 > {
231+ if ( ! predElse ) {
232+ return Object . defineProperties (
233+ isTupleOf ( predTup ) as Predicate < R1 > ,
234+ {
235+ name : {
236+ get : ( ) => `isReadonlyTupleOf(${ inspect ( predTup ) } )` ,
237+ } ,
151238 } ,
152- } ,
153- ) ;
239+ ) ;
240+ } else {
241+ return Object . defineProperties (
242+ isTupleOf ( predTup , predElse ) as unknown as Predicate < R2 > ,
243+ {
244+ name : {
245+ get : ( ) =>
246+ `isReadonlyTupleOf(${ inspect ( predTup ) } , ${ inspect ( predElse ) } )` ,
247+ } ,
248+ } ,
249+ ) ;
250+ }
154251}
155252
156253// https://stackoverflow.com/a/71700658/1273406
0 commit comments