From 6508f1df70594d8373e9575fd52e0958093b9b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Rish=C3=B8j?= Date: Sat, 27 Jul 2024 22:28:47 +0200 Subject: [PATCH 01/11] feat: add overloads for empty and non-empty arrays --- src/random/draw.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/random/draw.ts b/src/random/draw.ts index 6f2bfa11..e9c3837f 100644 --- a/src/random/draw.ts +++ b/src/random/draw.ts @@ -16,6 +16,8 @@ import { random } from 'radashi' * ``` * @version 12.1.0 */ +export function draw(array: readonly []): null; +export function draw(array: readonly T[]): T; export function draw(array: readonly T[]): T | null { const max = array.length if (max === 0) { From 6a3d413cfe391ad4795c2d4cb43ef0383a7be77f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Rish=C3=B8j?= Date: Sat, 27 Jul 2024 22:40:53 +0200 Subject: [PATCH 02/11] style: apply Biome fixes --- src/random/draw.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/random/draw.ts b/src/random/draw.ts index e9c3837f..7da6e287 100644 --- a/src/random/draw.ts +++ b/src/random/draw.ts @@ -16,8 +16,9 @@ import { random } from 'radashi' * ``` * @version 12.1.0 */ -export function draw(array: readonly []): null; -export function draw(array: readonly T[]): T; +export function draw(array: readonly []): null +export function draw(array: readonly T[]): T + export function draw(array: readonly T[]): T | null { const max = array.length if (max === 0) { From 77ee453576698e0a237cf46998422b8f72ea18a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Rish=C3=B8j?= Date: Tue, 6 Aug 2024 22:33:20 +0200 Subject: [PATCH 03/11] fix: overloads for empty and non-empty arrays --- src/random/draw.ts | 4 ++-- tests/random/draw.test-d.ts | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 tests/random/draw.test-d.ts diff --git a/src/random/draw.ts b/src/random/draw.ts index 7da6e287..280f4a21 100644 --- a/src/random/draw.ts +++ b/src/random/draw.ts @@ -16,8 +16,8 @@ import { random } from 'radashi' * ``` * @version 12.1.0 */ -export function draw(array: readonly []): null -export function draw(array: readonly T[]): T +export function draw(arr: readonly [T, ...T[]]): T +export function draw(array: readonly T[]): T | null export function draw(array: readonly T[]): T | null { const max = array.length diff --git a/tests/random/draw.test-d.ts b/tests/random/draw.test-d.ts new file mode 100644 index 00000000..c5b12632 --- /dev/null +++ b/tests/random/draw.test-d.ts @@ -0,0 +1,30 @@ +import * as _ from 'radashi' +import { expectTypeOf } from 'vitest' + +describe('draw types', () => { + test('returns null given empty input', () => { + const list: unknown[] = [] + const result = _.draw(list) + expect(result).toBeNull() + }) + test('return type is null for empty array literal', () => { + expectTypeOf(_.draw([])).toBeNull() + }) + test('return type is not null for non-empty array literal', () => { + expectTypeOf(_.draw([1, 2, 3])).toBeNumber() + }) + test('return type is possibly null for mutable array variables', () => { + const emptyList: number[] = [] + const filledList = [1, 2, 3] + expectTypeOf(_.draw(emptyList)).toEqualTypeOf() + expectTypeOf(_.draw(filledList)).toEqualTypeOf() + }) + test('return type is null for immutable empty array variables', () => { + const emptyList: number[] = [] as const + expectTypeOf(_.draw(emptyList)).toBeNull() + }) + test('return type is not null for immutable non-empty array variables', () => { + const filledList = [1, 2, 4] as const + expectTypeOf(_.draw(filledList)).toBeNumber() + }) +}) From 89872bd1bbf8f0d021744e30d57ac461e8423f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Rish=C3=B8j?= Date: Tue, 6 Aug 2024 22:52:20 +0200 Subject: [PATCH 04/11] style: format --- tests/random/draw.test-d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/random/draw.test-d.ts b/tests/random/draw.test-d.ts index c5b12632..10964815 100644 --- a/tests/random/draw.test-d.ts +++ b/tests/random/draw.test-d.ts @@ -16,8 +16,8 @@ describe('draw types', () => { test('return type is possibly null for mutable array variables', () => { const emptyList: number[] = [] const filledList = [1, 2, 3] - expectTypeOf(_.draw(emptyList)).toEqualTypeOf() - expectTypeOf(_.draw(filledList)).toEqualTypeOf() + expectTypeOf(_.draw(emptyList)).toEqualTypeOf() + expectTypeOf(_.draw(filledList)).toEqualTypeOf() }) test('return type is null for immutable empty array variables', () => { const emptyList: number[] = [] as const From b5126e106a00150f323f9543c7a87e083abab632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Rish=C3=B8j?= Date: Tue, 6 Aug 2024 22:58:40 +0200 Subject: [PATCH 05/11] fix: let test reflect broad type for immutable empty array argument --- tests/random/draw.test-d.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/random/draw.test-d.ts b/tests/random/draw.test-d.ts index 10964815..3695fc77 100644 --- a/tests/random/draw.test-d.ts +++ b/tests/random/draw.test-d.ts @@ -19,9 +19,10 @@ describe('draw types', () => { expectTypeOf(_.draw(emptyList)).toEqualTypeOf() expectTypeOf(_.draw(filledList)).toEqualTypeOf() }) - test('return type is null for immutable empty array variables', () => { + test('return type is possibly null for immutable empty array variables', () => { const emptyList: number[] = [] as const - expectTypeOf(_.draw(emptyList)).toBeNull() + // FIXME: Can be narrowed to `null` + expectTypeOf(_.draw(emptyList)).toEqualTypeOf() }) test('return type is not null for immutable non-empty array variables', () => { const filledList = [1, 2, 4] as const From 3d2277c08d5af9b3fe01128452aab22c6de6f2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Rish=C3=B8j?= Date: Wed, 7 Aug 2024 12:47:50 +0200 Subject: [PATCH 06/11] test: narrow type assertion Co-authored-by: Alec Larson <1925840+aleclarson@users.noreply.github.com> --- tests/random/draw.test-d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/random/draw.test-d.ts b/tests/random/draw.test-d.ts index 3695fc77..3264a1d7 100644 --- a/tests/random/draw.test-d.ts +++ b/tests/random/draw.test-d.ts @@ -26,6 +26,6 @@ describe('draw types', () => { }) test('return type is not null for immutable non-empty array variables', () => { const filledList = [1, 2, 4] as const - expectTypeOf(_.draw(filledList)).toBeNumber() + expectTypeOf(_.draw(filledList)).toEqualTypeOf<1 | 2 | 4>() }) }) From 6a87d203dbfb52ba74ea56a7382bb89c99106ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Rish=C3=B8j?= Date: Wed, 7 Aug 2024 14:01:23 +0200 Subject: [PATCH 07/11] test: test both `number[]` and `never[]` --- tests/random/draw.test-d.ts | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/tests/random/draw.test-d.ts b/tests/random/draw.test-d.ts index 3264a1d7..4e127fb0 100644 --- a/tests/random/draw.test-d.ts +++ b/tests/random/draw.test-d.ts @@ -2,30 +2,27 @@ import * as _ from 'radashi' import { expectTypeOf } from 'vitest' describe('draw types', () => { - test('returns null given empty input', () => { - const list: unknown[] = [] - const result = _.draw(list) - expect(result).toBeNull() - }) - test('return type is null for empty array literal', () => { + test('return type with literal argument', () => { expectTypeOf(_.draw([])).toBeNull() - }) - test('return type is not null for non-empty array literal', () => { expectTypeOf(_.draw([1, 2, 3])).toBeNumber() }) - test('return type is possibly null for mutable array variables', () => { + test('return type with mutable variable', () => { + const neverList: never[] = [] const emptyList: number[] = [] const filledList = [1, 2, 3] + + expectTypeOf(_.draw(neverList)).toEqualTypeOf() expectTypeOf(_.draw(emptyList)).toEqualTypeOf() expectTypeOf(_.draw(filledList)).toEqualTypeOf() }) - test('return type is possibly null for immutable empty array variables', () => { + test('return type with immutable variable', () => { + const neverList: never[] = [] as const const emptyList: number[] = [] as const - // FIXME: Can be narrowed to `null` + const filledList = [1, 2, 3] as const + + expectTypeOf(_.draw(neverList)).toBeNull() + // FIXME: Can this be narrowed to `null`? expectTypeOf(_.draw(emptyList)).toEqualTypeOf() - }) - test('return type is not null for immutable non-empty array variables', () => { - const filledList = [1, 2, 4] as const - expectTypeOf(_.draw(filledList)).toEqualTypeOf<1 | 2 | 4>() + expectTypeOf(_.draw(filledList)).toEqualTypeOf<1 | 2 | 3>() }) }) From 6317a1ec41388086af4463c6df85f1d9cd61bcb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Rish=C3=B8j?= Date: Wed, 7 Aug 2024 14:09:45 +0200 Subject: [PATCH 08/11] fix: consistent naming --- src/random/draw.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/random/draw.ts b/src/random/draw.ts index 280f4a21..4dbdca74 100644 --- a/src/random/draw.ts +++ b/src/random/draw.ts @@ -16,7 +16,7 @@ import { random } from 'radashi' * ``` * @version 12.1.0 */ -export function draw(arr: readonly [T, ...T[]]): T +export function draw(array: readonly [T, ...T[]]): T export function draw(array: readonly T[]): T | null export function draw(array: readonly T[]): T | null { From ce1895b005af0b83bb0dc6adabbfef91053e0f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Rish=C3=B8j?= Date: Thu, 15 Aug 2024 13:42:48 +0200 Subject: [PATCH 09/11] remove superfluous import Co-authored-by: Marlon Passos <1marlonpassos@gmail.com> --- tests/random/draw.test-d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/random/draw.test-d.ts b/tests/random/draw.test-d.ts index 4e127fb0..d92d5e41 100644 --- a/tests/random/draw.test-d.ts +++ b/tests/random/draw.test-d.ts @@ -1,5 +1,4 @@ import * as _ from 'radashi' -import { expectTypeOf } from 'vitest' describe('draw types', () => { test('return type with literal argument', () => { From 08358524c34eb762ef0fea5b9d36a19a589aebed Mon Sep 17 00:00:00 2001 From: Alec Larson <1925840+aleclarson@users.noreply.github.com> Date: Sun, 10 Nov 2024 09:23:57 -0500 Subject: [PATCH 10/11] refactor type tests --- tests/random/draw.test-d.ts | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/tests/random/draw.test-d.ts b/tests/random/draw.test-d.ts index d92d5e41..2e9c1a3d 100644 --- a/tests/random/draw.test-d.ts +++ b/tests/random/draw.test-d.ts @@ -1,27 +1,31 @@ import * as _ from 'radashi' -describe('draw types', () => { - test('return type with literal argument', () => { - expectTypeOf(_.draw([])).toBeNull() - expectTypeOf(_.draw([1, 2, 3])).toBeNumber() - }) - test('return type with mutable variable', () => { - const neverList: never[] = [] +describe('draw', () => { + test('variable with mutable array', () => { const emptyList: number[] = [] const filledList = [1, 2, 3] - expectTypeOf(_.draw(neverList)).toEqualTypeOf() expectTypeOf(_.draw(emptyList)).toEqualTypeOf() expectTypeOf(_.draw(filledList)).toEqualTypeOf() }) - test('return type with immutable variable', () => { - const neverList: never[] = [] as const - const emptyList: number[] = [] as const + + test('variable with empty array', () => { + const neverList: never[] = [] + const emptyList = [] as const + + expectTypeOf(_.draw(neverList)).toEqualTypeOf() + expectTypeOf(_.draw(emptyList)).toEqualTypeOf() + }) + + test('variable with tuple', () => { const filledList = [1, 2, 3] as const - expectTypeOf(_.draw(neverList)).toBeNull() - // FIXME: Can this be narrowed to `null`? - expectTypeOf(_.draw(emptyList)).toEqualTypeOf() expectTypeOf(_.draw(filledList)).toEqualTypeOf<1 | 2 | 3>() }) + + test('inlined array', () => { + expectTypeOf(_.draw([])).toEqualTypeOf() + expectTypeOf(_.draw([1, 2, 3])).toEqualTypeOf() + expectTypeOf(_.draw([1, 2, 3] as const)).toEqualTypeOf<1 | 2 | 3>() + }) }) From 3eccb8de482b04525e5a8392ee3de4b29ab5961d Mon Sep 17 00:00:00 2001 From: Alec Larson <1925840+aleclarson@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:36:31 -0500 Subject: [PATCH 11/11] improvements --- src/random/draw.ts | 9 ++++----- tests/random/draw.test-d.ts | 21 +++++++++------------ 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/random/draw.ts b/src/random/draw.ts index 4dbdca74..ddef019b 100644 --- a/src/random/draw.ts +++ b/src/random/draw.ts @@ -16,13 +16,12 @@ import { random } from 'radashi' * ``` * @version 12.1.0 */ -export function draw(array: readonly [T, ...T[]]): T -export function draw(array: readonly T[]): T | null - -export function draw(array: readonly T[]): T | null { +export function draw( + array: T, +): T extends readonly [any, ...any[]] ? T[number] : T[number] | null { const max = array.length if (max === 0) { - return null + return null as any } const index = random(0, max - 1) return array[index] diff --git a/tests/random/draw.test-d.ts b/tests/random/draw.test-d.ts index 2e9c1a3d..af3b4f94 100644 --- a/tests/random/draw.test-d.ts +++ b/tests/random/draw.test-d.ts @@ -2,30 +2,27 @@ import * as _ from 'radashi' describe('draw', () => { test('variable with mutable array', () => { - const emptyList: number[] = [] - const filledList = [1, 2, 3] + const array = [1, 2, 3] - expectTypeOf(_.draw(emptyList)).toEqualTypeOf() - expectTypeOf(_.draw(filledList)).toEqualTypeOf() + expectTypeOf(_.draw(array)).toEqualTypeOf() }) test('variable with empty array', () => { - const neverList: never[] = [] - const emptyList = [] as const + const emptyArray = [] as never[] + const emptyTuple = [] as const - expectTypeOf(_.draw(neverList)).toEqualTypeOf() - expectTypeOf(_.draw(emptyList)).toEqualTypeOf() + expectTypeOf(_.draw(emptyArray)).toEqualTypeOf() + expectTypeOf(_.draw(emptyTuple)).toEqualTypeOf() }) test('variable with tuple', () => { - const filledList = [1, 2, 3] as const + const tuple = [1, 2, 3] as const - expectTypeOf(_.draw(filledList)).toEqualTypeOf<1 | 2 | 3>() + expectTypeOf(_.draw(tuple)).toEqualTypeOf<1 | 2 | 3>() }) test('inlined array', () => { expectTypeOf(_.draw([])).toEqualTypeOf() - expectTypeOf(_.draw([1, 2, 3])).toEqualTypeOf() - expectTypeOf(_.draw([1, 2, 3] as const)).toEqualTypeOf<1 | 2 | 3>() + expectTypeOf(_.draw([1, 2, 3])).toEqualTypeOf<1 | 2 | 3>() }) })