@@ -79,6 +79,10 @@ export function isArrayOf<T>(
7979}
8080
8181export type TupleOf < T extends readonly Predicate < unknown > [ ] > = {
82+ - readonly [ P in keyof T ] : T [ P ] extends Predicate < infer U > ? U : never ;
83+ } ;
84+
85+ export type ReadonlyTupleOf < T extends readonly Predicate < unknown > [ ] > = {
8286 [ P in keyof T ] : T [ P ] extends Predicate < infer U > ? U : never ;
8387} ;
8488
@@ -92,7 +96,7 @@ export type TupleOf<T extends readonly Predicate<unknown>[]> = {
9296 * const a: unknown = [0, "a", true];
9397 * if (is.TupleOf(predTup)(a)) {
9498 * // a is narrowed to [number, string, boolean]
95- * const _: readonly [number, string, boolean] = a;
99+ * const _: [number, string, boolean] = a;
96100 * }
97101 * ```
98102 *
@@ -118,12 +122,50 @@ export function isTupleOf<T extends readonly Predicate<unknown>[]>(
118122 ) ;
119123}
120124
125+ /**
126+ * Return a type predicate function that returns `true` if the type of `x` is `ReadonlyTupleOf<T>`.
127+ *
128+ * ```ts
129+ * import { is } from "https://deno.land/x/unknownutil@$MODULE_VERSION/mod.ts";
130+ *
131+ * const predTup = [is.Number, is.String, is.Boolean] as const;
132+ * const a: unknown = [0, "a", true];
133+ * if (is.ReadonlyTupleOf(predTup)(a)) {
134+ * // a is narrowed to readonly [number, string, boolean]
135+ * const _: readonly [number, string, boolean] = a;
136+ * }
137+ * ```
138+ *
139+ * Note that `predTup` must be `readonly` (`as const`) to infer the type of `a` correctly.
140+ * TypeScript won't argues if `predTup` is not `readonly` because of its design limitation.
141+ * https://github.com/microsoft/TypeScript/issues/34274#issuecomment-541691353
142+ */
143+ export function isReadonlyTupleOf < T extends readonly Predicate < unknown > [ ] > (
144+ 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 ) } )` ,
151+ } ,
152+ } ,
153+ ) ;
154+ }
155+
121156// https://stackoverflow.com/a/71700658/1273406
122157export type UniformTupleOf <
123158 T ,
124159 N extends number ,
125160 R extends readonly T [ ] = [ ] ,
126- > = R [ "length" ] extends N ? R : UniformTupleOf < T , N , readonly [ T , ...R ] > ;
161+ > = R [ "length" ] extends N ? R : UniformTupleOf < T , N , [ T , ...R ] > ;
162+
163+ export type ReadonlyUniformTupleOf <
164+ T ,
165+ N extends number ,
166+ R extends readonly T [ ] = [ ] ,
167+ > = R [ "length" ] extends N ? R
168+ : ReadonlyUniformTupleOf < T , N , readonly [ T , ...R ] > ;
127169
128170/**
129171 * Return a type predicate function that returns `true` if the type of `x` is `UniformTupleOf<T>`.
@@ -134,12 +176,12 @@ export type UniformTupleOf<
134176 * const a: unknown = [0, 1, 2, 3, 4];
135177 * if (is.UniformTupleOf(5)(a)) {
136178 * // a is narrowed to [unknown, unknown, unknown, unknown, unknown]
137- * const _: readonly [unknown, unknown, unknown, unknown, unknown] = a;
179+ * const _: [unknown, unknown, unknown, unknown, unknown] = a;
138180 * }
139181 *
140182 * if (is.UniformTupleOf(5, is.Number)(a)) {
141183 * // a is narrowed to [number, number, number, number, number]
142- * const _: readonly [number, number, number, number, number] = a;
184+ * const _: [number, number, number, number, number] = a;
143185 * }
144186 * ```
145187 */
@@ -159,9 +201,41 @@ export function isUniformTupleOf<T, N extends number>(
159201}
160202
161203/**
162- * Synonym of `Record<string | number | symbol, T>`
204+ * Return a type predicate function that returns `true` if the type of `x` is `ReadonlyUniformTupleOf<T>`.
205+ *
206+ * ```ts
207+ * import { is } from "https://deno.land/x/unknownutil@$MODULE_VERSION/mod.ts";
208+ *
209+ * const a: unknown = [0, 1, 2, 3, 4];
210+ * if (is.ReadonlyUniformTupleOf(5)(a)) {
211+ * // a is narrowed to readonly [unknown, unknown, unknown, unknown, unknown]
212+ * const _: readonly [unknown, unknown, unknown, unknown, unknown] = a;
213+ * }
214+ *
215+ * if (is.ReadonlyUniformTupleOf(5, is.Number)(a)) {
216+ * // a is narrowed to readonly [number, number, number, number, number]
217+ * const _: readonly [number, number, number, number, number] = a;
218+ * }
219+ * ```
220+ */
221+ export function isReadonlyUniformTupleOf < T , N extends number > (
222+ n : N ,
223+ pred : Predicate < T > = isAny ,
224+ ) : Predicate < ReadonlyUniformTupleOf < T , N > > {
225+ return Object . defineProperties (
226+ isUniformTupleOf ( n , pred ) as Predicate < ReadonlyUniformTupleOf < T , N > > ,
227+ {
228+ name : {
229+ get : ( ) => `isReadonlyUniformTupleOf(${ n } , ${ inspect ( pred ) } )` ,
230+ } ,
231+ } ,
232+ ) ;
233+ }
234+
235+ /**
236+ * Synonym of `Record<PropertyKey, T>`
163237 */
164- export type RecordOf < T > = Record < string | number | symbol , T > ;
238+ export type RecordOf < T > = Record < PropertyKey , T > ;
165239
166240/**
167241 * Return `true` if the type of `x` is `RecordOf<unknown>`.
@@ -524,7 +598,9 @@ export default {
524598 Array : isArray ,
525599 ArrayOf : isArrayOf ,
526600 TupleOf : isTupleOf ,
601+ ReadonlyTupleOf : isReadonlyTupleOf ,
527602 UniformTupleOf : isUniformTupleOf ,
603+ ReadonlyUniformTupleOf : isReadonlyUniformTupleOf ,
528604 Record : isRecord ,
529605 RecordOf : isRecordOf ,
530606 ObjectOf : isObjectOf ,
0 commit comments