-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnamespace.ts
149 lines (129 loc) · 4.29 KB
/
namespace.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import { Option } from "../Option/namespace";
/**
*
* Type: A `NonEmptyArray<A>` is an immutable array where the elements have
* type `A` and the array contains at least one element.
*
*/
export type NonEmptyArray<A> = readonly [A, ...A[]];
/**
*
* Namespace: The `NonEmptyArray` namespace contains functions for
* `NonEmptyArray` values including constructors, type guards, conversions to
* other data types, and operations over the type.
*
*/
export namespace NonEmptyArray {
//
// Constructors
//
/** Create a NonEmptyArray with a `first` value and an optional `rest` array. */
export function nonEmptyArray<A>(first: A, rest: A[] = []): NonEmptyArray<A> {
return [first, ...rest];
}
/** Create a NonEmptyArray with a `first` value and an optional `rest` array. */
export const of = nonEmptyArray;
//
// Typeguards
//
/**
* Typeguard for a `NonEmptyArray`.
*
* Note that typeguards can't cast from mutable to readonly types. This means
* that if `a` has the type `number[]`, within the guard scape `a` will have
* the type `number[] & NonEmptyArray<number>`. The compiler will be aware that
* `a` is non-empty, but will also allow `a` to be mutated.
*/
export function isNonEmptyArray<A>(
a: ReadonlyArray<A>,
): a is NonEmptyArray<A>;
export function isNonEmptyArray(a: unknown): a is NonEmptyArray<unknown>;
export function isNonEmptyArray(a: unknown) {
return Array.isArray(a) && a.length > 0;
}
/** Typeguard for a `NonEmptyArray`. */
export const isType = isNonEmptyArray;
//
// Conversions
//
/**
* Return a shallow copy of `a` as a `NonEmptyArray` if it's non-empty, or
* return `Option.Nothing` if it's empty.
*/
export function fromArray<A extends NonEmptyArray<any>>(a: A): A;
export function fromArray<A>(a: readonly A[]): Option<NonEmptyArray<A>>;
export function fromArray(a: readonly unknown[]) {
const copy = [...a];
return isNonEmptyArray(copy) ? copy : undefined;
}
/**
* Assert that `a` is a `NonEmptyArray`. If the assertion holds then return a
* shallow copy of `a` with an updated type. If the assertion fails throw an
* error.
*/
export function coerceNonEmptyArray<A>(
a: ReadonlyArray<A>,
): NonEmptyArray<A> {
if (isNonEmptyArray(a)) return [...a];
throw new Error("Expected a NonEmptyArray");
}
/**
* Assert that `a` is a `NonEmptyArray`. If the assertion holds then return a
* shallow copy of `a` with an updated type. If the assertion fails throw an
* error.
*/
export const coerce = coerceNonEmptyArray;
//
// Operations
//
/** Get the first element in a `NonEmptyArray`. */
export function first<A>([f]: NonEmptyArray<A>): A {
return f;
}
/** Get the last element in a `NonEmptyArray`. */
export function last<A>(a: NonEmptyArray<A>): A {
return a[a.length - 1] as A;
}
/** Return a new, possibly empty, Array with all but the last element. */
export function front<A>(a: NonEmptyArray<A>): readonly A[] {
return a.slice(0, a.length - 1);
}
/** Return a new, possibly empty, Array with all but the first element. */
export function tail<A>([_h, ...t]: NonEmptyArray<A>): readonly A[] {
return t;
}
/**
* Apply `fn` to each element in the `NonEmptyArray`. Unlike
* `Array.prototype.map`, this function preserves the `NonEmptyArray` type,
* instead of returning an `Array`.
*/
export function map<A, B>(
a: NonEmptyArray<A>,
fn: (a: A, index?: number, array?: NonEmptyArray<A>) => B,
): NonEmptyArray<B> {
return a.map(fn as any) as any;
}
/** Reverse the order of a `NonEmptyArray`, returning a shallow copy. */
export function reverse<A>(a: NonEmptyArray<A>): NonEmptyArray<A> {
return [...a].reverse() as any;
}
/** Concat two arrays, one of which must be a `NonEmptyArray`. */
export function concat<A>(
a1: NonEmptyArray<A>,
a2: ReadonlyArray<A>,
): NonEmptyArray<A>;
export function concat<A>(
a1: ReadonlyArray<A>,
a2: NonEmptyArray<A>,
): NonEmptyArray<A>;
export function concat(
a1: ReadonlyArray<unknown>,
a2: ReadonlyArray<unknown>,
) {
return [...a1, ...a2] as any;
}
/** Sort a `NonEmptyArray`, returning a shallow copy. */
export function sort<A>(a: NonEmptyArray<A>): NonEmptyArray<A> {
return [...a].sort() as any;
}
}