feat: typechecking tests
This commit is contained in:
@@ -18,3 +18,32 @@ export namespace HKT {
|
||||
(T & { _t: t })["new"]
|
||||
>;
|
||||
}
|
||||
|
||||
if (import.meta.vitest) {
|
||||
const { test, expectTypeOf } = import.meta.vitest;
|
||||
|
||||
interface Identity extends HKT {
|
||||
new: (t: HKT.T<this>) => typeof t;
|
||||
}
|
||||
|
||||
interface ReplaceWith<T extends number> extends HKT<number, T> {
|
||||
new: () => T;
|
||||
}
|
||||
|
||||
test("Identity <T> = T", () => {
|
||||
type V = 10;
|
||||
expectTypeOf<HKT.Apply<Identity, V>>().toEqualTypeOf<V>();
|
||||
|
||||
expectTypeOf<
|
||||
HKT.Apply<Identity, Identity>
|
||||
>().toEqualTypeOf<Identity>();
|
||||
});
|
||||
|
||||
test("Generic <T><U> = T", () => {
|
||||
type V = 10;
|
||||
|
||||
expectTypeOf<
|
||||
HKT.Apply<ReplaceWith<V>, 14>
|
||||
>().toEqualTypeOf<V>();
|
||||
});
|
||||
}
|
||||
|
||||
+43
-27
@@ -336,6 +336,10 @@ export interface Array extends Mixin.HKT {
|
||||
* @param index The index of the current element in the array
|
||||
* @param array The array being iterated
|
||||
*/
|
||||
callback: (
|
||||
value: U,
|
||||
...args: IterArgs<T>
|
||||
) => U,
|
||||
) => Return<U, typeof t>;
|
||||
} & HidePrototype;
|
||||
/**
|
||||
@@ -565,7 +569,7 @@ export const Array = Mixin<Array>((value, $, fluent) => {
|
||||
});
|
||||
|
||||
if (import.meta.vitest) {
|
||||
const { test, expect, vi } = import.meta.vitest;
|
||||
const { test, expect, expectTypeOf, vi } = import.meta.vitest;
|
||||
|
||||
const registry = [Array] as const;
|
||||
const $ = makeFluent(registry);
|
||||
@@ -573,10 +577,20 @@ if (import.meta.vitest) {
|
||||
test("at()", () => {
|
||||
const arr = [1, 2, 3] as const;
|
||||
|
||||
expect($(arr).at(0).value).toBe(arr[0]);
|
||||
const first = $(arr).at(0).value;
|
||||
expect(first).toBe(arr[0]);
|
||||
expectTypeOf(first).toEqualTypeOf(arr[0]);
|
||||
|
||||
expect($(arr).at(5).value).toBe(undefined);
|
||||
|
||||
expect($(arr).at(-1).value).toBe(arr[arr.length - 1]);
|
||||
const last = $(arr).at(-1).value;
|
||||
expect(last).toBe(arr[arr.length - 1]);
|
||||
expectTypeOf(last).toEqualTypeOf<
|
||||
typeof arr extends readonly [...unknown[], infer Last]
|
||||
? Last
|
||||
: never
|
||||
>();
|
||||
|
||||
expect($(arr).at(-5).value).toBe(undefined);
|
||||
});
|
||||
|
||||
@@ -598,13 +612,13 @@ if (import.meta.vitest) {
|
||||
});
|
||||
|
||||
test("extend()", () => {
|
||||
const arr = [1, 2];
|
||||
const arr = [1, 2] as const;
|
||||
|
||||
const copies = 2;
|
||||
const copies = 2 as const;
|
||||
const expected: number[] = new globalThis.Array<number[]>(
|
||||
copies + 1,
|
||||
)
|
||||
.fill(arr)
|
||||
.fill([...arr])
|
||||
.flat(1);
|
||||
|
||||
expect($(arr).extend(copies).value).toMatchObject(expected);
|
||||
@@ -615,9 +629,10 @@ if (import.meta.vitest) {
|
||||
const arr = [1, 2, 3, 4, 5] as const;
|
||||
const isEven = vi.fn((v: number) => v % 2 === 0);
|
||||
|
||||
expect($(arr).map(isEven).value).toMatchObject(
|
||||
arr.map(isEven),
|
||||
);
|
||||
const v = $(arr).map(isEven).value;
|
||||
expect(v).toMatchObject(arr.map(isEven));
|
||||
expectTypeOf(v).toExtend<boolean[]>();
|
||||
expectTypeOf(v).not.toEqualTypeOf<boolean[]>();
|
||||
|
||||
for (let i = 0; i < arr.length; i++)
|
||||
expect(isEven).toHaveBeenNthCalledWith(
|
||||
@@ -632,9 +647,10 @@ if (import.meta.vitest) {
|
||||
const arr = [5, 4, 3, 2, 1] as const;
|
||||
const isEven = vi.fn((v: number) => v % 2 === 0);
|
||||
|
||||
expect($(arr).filter(isEven).value).toMatchObject(
|
||||
arr.filter(isEven),
|
||||
);
|
||||
const v = $(arr).filter(isEven).value;
|
||||
expect(v).toMatchObject(arr.filter(isEven));
|
||||
expectTypeOf(v).toExtend<readonly number[]>();
|
||||
expectTypeOf(v).not.toEqualTypeOf<readonly number[]>();
|
||||
|
||||
for (let i = 0; i < arr.length; i++)
|
||||
expect(isEven).toHaveBeenNthCalledWith(
|
||||
@@ -645,9 +661,12 @@ if (import.meta.vitest) {
|
||||
);
|
||||
|
||||
const dirty = [1, null, 6, undefined, 2] as const;
|
||||
expect($(dirty).filter.some().value).toMatchObject(
|
||||
const clean = $(dirty).filter.some().value;
|
||||
expect(clean).toMatchObject(
|
||||
dirty.filter((v) => v !== null && v !== undefined),
|
||||
);
|
||||
expectTypeOf(clean).toExtend<readonly number[]>();
|
||||
expectTypeOf(clean).not.toEqualTypeOf<readonly number[]>();
|
||||
});
|
||||
|
||||
test("reduce()", () => {
|
||||
@@ -693,7 +712,9 @@ if (import.meta.vitest) {
|
||||
test("length()", () => {
|
||||
const arr = [3, 3, 3] as const;
|
||||
|
||||
expect($(arr).length().value).toBe(arr.length);
|
||||
const v = $(arr).length().value;
|
||||
expect(v).toBe(arr.length);
|
||||
expectTypeOf(v).toEqualTypeOf(arr.length);
|
||||
});
|
||||
|
||||
test("sort()", () => {
|
||||
@@ -701,9 +722,10 @@ if (import.meta.vitest) {
|
||||
|
||||
const callback = (a: number) => (a % 2 === 0 ? -1 : 1);
|
||||
|
||||
expect($(numbers).sort(callback).value).toMatchObject(
|
||||
numbers.toSorted(callback),
|
||||
);
|
||||
const v = $(numbers).sort(callback).value;
|
||||
expect(v).toMatchObject(numbers.toSorted(callback));
|
||||
expectTypeOf(v).toExtend<readonly number[]>();
|
||||
expectTypeOf(v).not.toEqualTypeOf<readonly number[]>();
|
||||
|
||||
expect($(numbers).sort.ascending().value).toMatchObject(
|
||||
numbers.toSorted((a, b) => a - b),
|
||||
@@ -736,15 +758,9 @@ if (import.meta.vitest) {
|
||||
test("reverse()", () => {
|
||||
const array = [1, 2, 3, 4, 5] as const;
|
||||
|
||||
expect($(array).reverse().value).toMatchObject(
|
||||
array.toReversed(),
|
||||
);
|
||||
});
|
||||
|
||||
test("length()", () => {
|
||||
const length = 3;
|
||||
const array = new globalThis.Array<number>(length).fill(5);
|
||||
|
||||
expect($(array).length().value).toBe(length);
|
||||
const v = $(array).reverse().value;
|
||||
expect(v).toMatchObject(array.toReversed());
|
||||
expectTypeOf(v).toExtend<readonly number[]>();
|
||||
expectTypeOf(v).not.toEqualTypeOf<readonly number[]>();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ if (import.meta.vitest) {
|
||||
const $ = makeFluent(registry);
|
||||
|
||||
test(".awaited", async () => {
|
||||
const value = 10;
|
||||
const value = 10 as const;
|
||||
const promise = new Promise<number>((r) => {
|
||||
r(value);
|
||||
});
|
||||
|
||||
+77
-19
@@ -6,15 +6,44 @@ import {
|
||||
type Props,
|
||||
type Return,
|
||||
} from "../base/mixin";
|
||||
import { assertType } from "../internal";
|
||||
import { assertType, type MaxDepth } from "../internal";
|
||||
import { applySwizzle, type Swizzle } from "./math/swizzle";
|
||||
|
||||
type Vec<
|
||||
T,
|
||||
N extends number,
|
||||
TOut extends T[] = [],
|
||||
> = TOut["length"] extends N
|
||||
? TOut
|
||||
: TOut["length"] extends MaxDepth
|
||||
? T[]
|
||||
: Vec<T, N, [...TOut, T]>;
|
||||
|
||||
type Inc<N extends number> = [...Vec<null, N>, null]["length"];
|
||||
|
||||
type Sign<T extends number> = T extends 0
|
||||
? 0
|
||||
: `${T}` extends `-${number}`
|
||||
? -1
|
||||
: 1;
|
||||
|
||||
type Negate<T extends number> = T extends 0
|
||||
? 0
|
||||
: `${T}` extends `-${infer N extends number}`
|
||||
? N
|
||||
: `-${T}` extends `${infer N extends number}`
|
||||
? N
|
||||
: number;
|
||||
|
||||
type Floor<T extends number> =
|
||||
`${T}` extends `${infer N extends number}.${number}` ? N : T;
|
||||
type Ceil<T extends number> =
|
||||
`${T}` extends `${infer N extends number}.${number}` ? Inc<N> : T;
|
||||
type Round<T extends number> =
|
||||
`${T}` extends `${infer N extends number}.${0 | 1 | 2 | 3 | 4}${number | ""}`
|
||||
? N
|
||||
: Ceil<T>;
|
||||
|
||||
export interface Math extends Mixin.HKT {
|
||||
new: (t: HKT.T<this>) => Input<typeof t> extends infer T
|
||||
? T extends number
|
||||
@@ -38,18 +67,23 @@ export interface Math extends Mixin.HKT {
|
||||
) => Return<number, typeof t>;
|
||||
log: (base?: number) => Return<number, typeof t>;
|
||||
|
||||
isEqualApprox: (
|
||||
comparesTo: (
|
||||
other: number,
|
||||
delta?: number,
|
||||
) => Return<boolean, typeof t>;
|
||||
|
||||
floor: () => Return<number, typeof t>;
|
||||
ceil: () => Return<number, typeof t>;
|
||||
round: () => Return<number, typeof t>;
|
||||
floor: () => Return<Floor<T>, typeof t>;
|
||||
ceil: () => Return<Ceil<T>, typeof t>;
|
||||
round: () => Return<Round<T>, typeof t>;
|
||||
fround: () => Return<number, typeof t>;
|
||||
|
||||
abs: () => Return<number, typeof t>;
|
||||
negate: () => Return<number, typeof t>;
|
||||
abs: () => Return<
|
||||
`${T}` extends `-${infer N extends number}`
|
||||
? N
|
||||
: T,
|
||||
typeof t
|
||||
>;
|
||||
negate: () => Return<Negate<T>, typeof t>;
|
||||
sign: () => Return<Sign<T>, typeof t>;
|
||||
|
||||
min: (
|
||||
@@ -118,7 +152,7 @@ export const Math = Mixin<Math>((value, $, fluent) => {
|
||||
return fluent(M.log(value));
|
||||
};
|
||||
|
||||
$.isEqualApprox = (
|
||||
$.comparesTo = (
|
||||
other: number,
|
||||
delta: number = Number.EPSILON,
|
||||
) => {
|
||||
@@ -179,7 +213,7 @@ export const Math = Mixin<Math>((value, $, fluent) => {
|
||||
});
|
||||
|
||||
if (import.meta.vitest) {
|
||||
const { test, expect } = import.meta.vitest;
|
||||
const { test, expect, expectTypeOf } = import.meta.vitest;
|
||||
|
||||
const registry = [Math] as const;
|
||||
const $ = makeFluent(registry);
|
||||
@@ -204,38 +238,62 @@ if (import.meta.vitest) {
|
||||
expect($(1000).log(10).value).toBeCloseTo(M.log10(1000));
|
||||
});
|
||||
|
||||
test("equalApprox()", () => {
|
||||
test("comparesTo()", () => {
|
||||
const a = 0.1 + 0.2;
|
||||
const target = 0.3;
|
||||
|
||||
expect($(a).isEqualApprox(target).value).toBe(true);
|
||||
expect($(a).comparesTo(target).value).toBe(true);
|
||||
|
||||
expect($(a).isEqualApprox(1, 1).value).toBe(true);
|
||||
expect($(a).comparesTo(1, 1).value).toBe(true);
|
||||
|
||||
expect(
|
||||
$(a).isEqualApprox(target + Number.EPSILON * 2).value,
|
||||
$(a).comparesTo(target + Number.EPSILON * 2).value,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
t("floor", 1.7, 1);
|
||||
t("ceil", 1.7, 2);
|
||||
test("floor()", () => {
|
||||
expect($(1).floor().value).toBe(1);
|
||||
expect($(1.7).floor().value).toBe(1);
|
||||
|
||||
expectTypeOf($(5.8).floor().value).toEqualTypeOf<5>();
|
||||
expectTypeOf($(5).floor().value).toEqualTypeOf<5>();
|
||||
});
|
||||
test("ceil()", () => {
|
||||
expect($(1).ceil().value).toBe(1);
|
||||
expect($(1.7).ceil().value).toBe(2);
|
||||
|
||||
expectTypeOf($(5).ceil().value).toEqualTypeOf<5>();
|
||||
expectTypeOf($(5.8).ceil().value).toEqualTypeOf<6>();
|
||||
});
|
||||
test("round()", () => {
|
||||
expect($(1.3).round().value).toBe(1);
|
||||
expect($(4.5).round().value).toBe(5);
|
||||
|
||||
expectTypeOf($(1.3).round().value).toEqualTypeOf<1>();
|
||||
expectTypeOf($(4.5).round().value).toEqualTypeOf<5>();
|
||||
});
|
||||
t("fround", 6.45, M.fround(6.45));
|
||||
|
||||
test("abs()", () => {
|
||||
const v = 4;
|
||||
const positive = 4 as const;
|
||||
const negative = -4 as const;
|
||||
|
||||
expect($(v).abs().value).toBe(v);
|
||||
expect($(-v).abs().value).toBe(v);
|
||||
const a = $(positive).abs().value;
|
||||
expect(a).toBe(positive);
|
||||
expectTypeOf(a).toEqualTypeOf(positive);
|
||||
|
||||
const b = $(negative).abs().value;
|
||||
expect(b).toBe(positive);
|
||||
expectTypeOf(b).toEqualTypeOf(positive);
|
||||
});
|
||||
test("negate()", () => {
|
||||
const v = 7;
|
||||
const v = 7 as const;
|
||||
|
||||
expect($(v).negate().value).toBe(-v);
|
||||
expect($(v).negate().negate().value).toBe(v);
|
||||
|
||||
expectTypeOf($(4).negate().value).toEqualTypeOf<-4>();
|
||||
expectTypeOf($(-4).negate().value).toEqualTypeOf<4>();
|
||||
});
|
||||
test("sign()", () => {
|
||||
expect($(-4).sign().value).toBe(-1);
|
||||
|
||||
@@ -160,7 +160,7 @@ export function applySwizzle(
|
||||
$: object,
|
||||
fluent: (value: unknown) => never,
|
||||
) {
|
||||
const length = value.length;
|
||||
const length = globalThis.Math.min(value.length, axis.length);
|
||||
const state = new Array<number>(length).fill(-1);
|
||||
|
||||
main: do {
|
||||
@@ -218,7 +218,7 @@ export function applySwizzle(
|
||||
}
|
||||
|
||||
if (import.meta.vitest) {
|
||||
const { test, expect } = import.meta.vitest;
|
||||
const { test, expect, expectTypeOf } = import.meta.vitest;
|
||||
|
||||
const registry = [Math] as const;
|
||||
const $ = makeFluent(registry);
|
||||
@@ -230,9 +230,12 @@ if (import.meta.vitest) {
|
||||
const vec = [x, y] as const;
|
||||
|
||||
expect($(vec).xx.value).toMatchObject([x, x]);
|
||||
expect($(vec).yx.value).toMatchObject([y, x]);
|
||||
expect($(vec).yy.value).toMatchObject([y, y]);
|
||||
|
||||
const yx = $(vec).yx.value;
|
||||
expect(yx).toMatchObject([y, x]);
|
||||
expectTypeOf(yx).toEqualTypeOf<[typeof y, typeof x]>();
|
||||
|
||||
const large = [x, y, x, y] as const;
|
||||
|
||||
expect($(large).wzyx.value).toMatchObject([y, x, y, x]);
|
||||
@@ -246,27 +249,75 @@ if (import.meta.vitest) {
|
||||
const x = 5 as const;
|
||||
const arr = [x] as const;
|
||||
|
||||
expect($(arr).x.value).toBe(x);
|
||||
const v = $(arr).x.value;
|
||||
expect(v).toBe(x);
|
||||
expectTypeOf(v).toEqualTypeOf(x);
|
||||
});
|
||||
|
||||
test(".y", () => {
|
||||
const y = 2 as const;
|
||||
const arr = [y, y] as const;
|
||||
|
||||
expect($(arr).y.value).toBe(y);
|
||||
const v = $(arr).y.value;
|
||||
expect(v).toBe(y);
|
||||
expectTypeOf(v).toEqualTypeOf(y);
|
||||
});
|
||||
|
||||
test(".z", () => {
|
||||
const z = 9 as const;
|
||||
const arr = [z, z, z] as const;
|
||||
|
||||
expect($(arr).z.value).toBe(z);
|
||||
const v = $(arr).z.value;
|
||||
expect(v).toBe(z);
|
||||
expectTypeOf(v).toEqualTypeOf(z);
|
||||
});
|
||||
|
||||
test(".w", () => {
|
||||
const w = 0 as const;
|
||||
const arr = [w, w, w, w] as const;
|
||||
|
||||
expect($(arr).w.value).toBe(w);
|
||||
const v = $(arr).w.value;
|
||||
expect(v).toBe(w);
|
||||
expectTypeOf(v).toEqualTypeOf(w);
|
||||
});
|
||||
|
||||
test("Next", () => {
|
||||
type N = 3;
|
||||
|
||||
expectTypeOf<
|
||||
Next<N, [undefined, undefined, undefined]>
|
||||
>().toEqualTypeOf<[0, undefined, undefined]>();
|
||||
|
||||
expectTypeOf<
|
||||
Next<N, [2, undefined, undefined]>
|
||||
>().toEqualTypeOf<[undefined, 0, undefined]>();
|
||||
|
||||
expectTypeOf<Next<N, [2, 2, 2]>>().toEqualTypeOf<null>();
|
||||
});
|
||||
|
||||
test("Key", () => {
|
||||
expectTypeOf<Key<[1, 2, undefined]>>().toEqualTypeOf<"yz">();
|
||||
expectTypeOf<Key<[1, 1, 1]>>().toEqualTypeOf<"yyy">();
|
||||
});
|
||||
|
||||
test("Sequence", () => {
|
||||
type In = [1, 2, 3, 4, 5];
|
||||
type Out = [5, 4, 3, 2, 1];
|
||||
type Reverse = [4, 3, 2, 1, 0];
|
||||
|
||||
expectTypeOf<Sequence<In, Reverse>>().toEqualTypeOf<Out>();
|
||||
});
|
||||
|
||||
test("Primative", () => {
|
||||
interface Props {
|
||||
value: [number, number];
|
||||
meta: { registry: [] };
|
||||
}
|
||||
|
||||
type V = Primative<[number, number], Props>[1];
|
||||
|
||||
expectTypeOf<V>().toHaveProperty("x");
|
||||
expectTypeOf<V>().toHaveProperty("y");
|
||||
expectTypeOf<V>().not.toHaveProperty("z");
|
||||
});
|
||||
}
|
||||
|
||||
+34
-19
@@ -34,7 +34,7 @@ interface Where<T, t extends Props> {
|
||||
* expect(version).toBe("1.2.4");
|
||||
* @from {@link Optional `Optional`}
|
||||
*/
|
||||
where: <U = null>(
|
||||
where: <const U = null>(
|
||||
callback: (v: T) => boolean,
|
||||
fallback?: U,
|
||||
) => Return<T | U, t>;
|
||||
@@ -122,14 +122,14 @@ export interface Optional extends Mixin.HKT {
|
||||
* @from {@link Optional `Optional`}
|
||||
* @example
|
||||
* ```ts
|
||||
* const array = [1, 2, 3];
|
||||
* const element: number | undefined = array[1];
|
||||
* const array = [1, 2, 3] as const;
|
||||
* const element = array.at(1);
|
||||
*
|
||||
* const v = $(element).assert("index within bounds").value;
|
||||
* expect(v).toBe(2);
|
||||
*
|
||||
* expect(() => {
|
||||
* $(array[6]).assert()
|
||||
* $(array.at(6)).assert()
|
||||
* }).toThrow()
|
||||
* ```
|
||||
*/
|
||||
@@ -142,14 +142,14 @@ export interface Optional extends Mixin.HKT {
|
||||
* @from {@link Optional `Optional`}
|
||||
* @example
|
||||
* ```ts
|
||||
* const array = [1, 2, 3];
|
||||
* const element: number | undefined = array[5];
|
||||
* const array = [1, 2, 3] as const;
|
||||
* const element = array.at(5);
|
||||
*
|
||||
* const v = $(element).assert.none("index out of bounds").value;
|
||||
* expect(v).toBe(undefined);
|
||||
*
|
||||
* expect(() => {
|
||||
* $(array[1]).assert.none()
|
||||
* $(array.at(1)).assert.none()
|
||||
* }).toThrow()
|
||||
* ```
|
||||
*/
|
||||
@@ -223,17 +223,17 @@ export const Optional = Mixin<Optional>((value, $, fluent) => {
|
||||
});
|
||||
|
||||
if (import.meta.vitest) {
|
||||
const { test, expect, vi } = import.meta.vitest;
|
||||
const { test, expect, expectTypeOf, vi } = import.meta.vitest;
|
||||
|
||||
const registry = [Optional] as const;
|
||||
const $ = makeFluent(registry);
|
||||
|
||||
type Value = number | null;
|
||||
const Value = <const T>(v: T) => v as T | null;
|
||||
|
||||
test("and()", () => {
|
||||
const value = 10;
|
||||
const some = value as Value;
|
||||
const none = null as Value;
|
||||
const some = Value(value);
|
||||
const none = Value(null);
|
||||
|
||||
const callback = (v: number) => v + 5;
|
||||
|
||||
@@ -242,25 +242,40 @@ if (import.meta.vitest) {
|
||||
});
|
||||
|
||||
test("or()", () => {
|
||||
const some = 10 as Value;
|
||||
const none = null as Value;
|
||||
const value = 10 as const;
|
||||
const some = Value(value);
|
||||
const none = Value(null);
|
||||
|
||||
const fallback = 15 as const;
|
||||
|
||||
expect($(some).or(fallback).value).toBe(some);
|
||||
expect($(none).or(fallback).value).toBe(fallback);
|
||||
const a = $(some).or(fallback).value;
|
||||
expect(a).toBe(some);
|
||||
expectTypeOf(a).toEqualTypeOf<
|
||||
typeof value | typeof fallback
|
||||
>();
|
||||
|
||||
const b = $(none).or(fallback).value;
|
||||
expect(b).toBe(fallback);
|
||||
expectTypeOf(b).toEqualTypeOf<typeof fallback>();
|
||||
|
||||
const callback = vi.fn(() => fallback);
|
||||
|
||||
expect($(some).or.else(callback).value).toBe(some);
|
||||
expect($(none).or.else(callback).value).toBe(fallback);
|
||||
const c = $(some).or.else(callback).value;
|
||||
expect(c).toBe(some);
|
||||
expectTypeOf(c).toEqualTypeOf<
|
||||
typeof value | typeof fallback
|
||||
>();
|
||||
|
||||
const d = $(none).or.else(callback).value;
|
||||
expect(d).toBe(fallback);
|
||||
expectTypeOf(d).toEqualTypeOf<typeof fallback>();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
test("where()", () => {
|
||||
const even = 4;
|
||||
const odd = 7;
|
||||
const even = 4 as const;
|
||||
const odd = 7 as const;
|
||||
|
||||
const isEven = (v: number) => v % 2 === 0;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user