Skip to content

Commit f8ae35d

Browse files
authored
Merge pull request #26 from lambdalisue/add-is-mono-typed-tuple
👍 Add `isUniformTupleOf`
2 parents ea6c279 + 55705ce commit f8ae35d

File tree

2 files changed

+57
-0
lines changed

2 files changed

+57
-0
lines changed

is.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,39 @@ export function isTupleOf<T extends readonly Predicate<unknown>[]>(
8282
};
8383
}
8484

85+
// https://stackoverflow.com/a/71700658/1273406
86+
export type UniformTupleOf<
87+
T,
88+
N extends number,
89+
R extends readonly T[] = [],
90+
> = R["length"] extends N ? R : UniformTupleOf<T, N, readonly [T, ...R]>;
91+
92+
/**
93+
* Return a type predicate function that returns `true` if the type of `x` is `UniformTupleOf<T>`.
94+
*
95+
* ```ts
96+
* import is from "./is.ts";
97+
*
98+
* const a: unknown = [0, 1, 2, 3, 4];
99+
* if (is.UniformTupleOf(5)(a)) {
100+
* // a is narrowed to [unknown, unknown, unknown, unknown, unknown]
101+
* const _: readonly [unknown, unknown, unknown, unknown, unknown] = a;
102+
* }
103+
*
104+
* if (is.UniformTupleOf(5, is.Number)(a)) {
105+
* // a is narrowed to [number, number, number, number, number]
106+
* const _: readonly [number, number, number, number, number] = a;
107+
* }
108+
* ```
109+
*/
110+
export function isUniformTupleOf<T, N extends number>(
111+
n: N,
112+
pred: Predicate<T> = (_x: unknown): _x is T => true,
113+
): Predicate<UniformTupleOf<T, N>> {
114+
const predTup = Array(n).fill(pred);
115+
return isTupleOf(predTup) as Predicate<UniformTupleOf<T, N>>;
116+
}
117+
85118
/**
86119
* Synonym of `Record<string | number | symbol, T>`
87120
*/
@@ -248,6 +281,7 @@ export default {
248281
Array: isArray,
249282
ArrayOf: isArrayOf,
250283
TupleOf: isTupleOf,
284+
UniformTupleOf: isUniformTupleOf,
251285
Record: isRecord,
252286
RecordOf: isRecordOf,
253287
ObjectOf: isObjectOf,

is_test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import is, {
2020
isSymbol,
2121
isTupleOf,
2222
isUndefined,
23+
isUniformTupleOf,
2324
Predicate,
2425
} from "./is.ts";
2526

@@ -119,6 +120,28 @@ Deno.test("isTupleOf<T>", async (t) => {
119120
});
120121
});
121122

123+
Deno.test("isUniformTupleOf<T>", async (t) => {
124+
await t.step("returns proper type predicate", () => {
125+
const a: unknown = [0, 1, 2, 3, 4];
126+
if (isUniformTupleOf(5)(a)) {
127+
const _: readonly [unknown, unknown, unknown, unknown, unknown] = a;
128+
}
129+
130+
if (isUniformTupleOf(5, isNumber)(a)) {
131+
const _: readonly [number, number, number, number, number] = a;
132+
}
133+
});
134+
await t.step("returns true on mono-typed T tuple", () => {
135+
assertEquals(isUniformTupleOf(3)([0, 1, 2]), true);
136+
assertEquals(isUniformTupleOf(3, is.Number)([0, 1, 2]), true);
137+
});
138+
await t.step("returns false on non mono-typed T tuple", () => {
139+
assertEquals(isUniformTupleOf(4)([0, 1, 2]), false);
140+
assertEquals(isUniformTupleOf(4)([0, 1, 2, 3, 4]), false);
141+
assertEquals(isUniformTupleOf(3, is.Number)(["a", "b", "c"]), false);
142+
});
143+
});
144+
122145
Deno.test("isRecord", async (t) => {
123146
await testWithExamples(t, isRecord, ["record"]);
124147
});

0 commit comments

Comments
 (0)