Skip to content

Commit 70da8d5

Browse files
committed
Typescriptify array-utils 🐬
1 parent 0363aa6 commit 70da8d5

File tree

2 files changed

+92
-64
lines changed

2 files changed

+92
-64
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
function detect(enumerable, callback) {
2-
if (enumerable.detect) {
1+
interface Detectable<T> {
2+
detect(cb: (val: T) => boolean): T
3+
}
4+
5+
interface HasLength<T> {
6+
[key: number]: T
7+
length: number
8+
}
9+
10+
export function detect<T>(enumerable: Detectable<T> | HasLength<T>, callback: (val: T) => boolean): T | undefined {
11+
if ('detect' in enumerable) {
312
return enumerable.detect(callback)
413
} else {
514
for (let i = 0; i < enumerable.length; i++) {
@@ -10,8 +19,12 @@ function detect(enumerable, callback) {
1019
}
1120
}
1221

13-
function any(enumerable, callback) {
14-
if (enumerable.any) {
22+
interface Anyable<T> {
23+
any(cb: (val: T) => boolean): boolean
24+
}
25+
26+
export function any<T>(enumerable: Anyable<T> | HasLength<T>, callback: (val: T) => boolean): boolean {
27+
if ('any' in enumerable) {
1528
return enumerable.any(callback)
1629
}
1730

@@ -24,8 +37,12 @@ function any(enumerable, callback) {
2437
return false
2538
}
2639

27-
function every(enumerable, callback) {
28-
if (enumerable.every) {
40+
interface Everyable<T> {
41+
every(cb: (val: T) => boolean): boolean
42+
}
43+
44+
export function every<T>(enumerable: Everyable<T> | HasLength<T>, callback: (val: T) => boolean): boolean {
45+
if ('every' in enumerable) {
2946
return enumerable.every(callback)
3047
}
3148

@@ -34,20 +51,25 @@ function every(enumerable, callback) {
3451
return false
3552
}
3653
}
54+
3755
return true
3856
}
3957

40-
function toArray(arrayLike) {
58+
export function toArray<T>(arrayLike: ArrayLike<T>): T[] {
4159
return Array.prototype.slice.call(arrayLike)
4260
}
4361

62+
interface ForEachable<T> {
63+
forEach(cb: (val: T, idx: number) => void): void
64+
}
65+
4466
/**
4567
* Useful for array-like things that aren't
4668
* actually arrays, like NodeList
4769
* @private
4870
*/
49-
function forEach(enumerable, callback) {
50-
if (enumerable.forEach) {
71+
export function forEach<T>(enumerable: ForEachable<T> | HasLength<T>, callback: (val: T, idx: number) => void): void {
72+
if ('forEach' in enumerable) {
5173
enumerable.forEach(callback)
5274
} else {
5375
for (let i = 0; i < enumerable.length; i++) {
@@ -56,56 +78,68 @@ function forEach(enumerable, callback) {
5678
}
5779
}
5880

59-
function filter(enumerable, conditionFn) {
60-
const filtered = []
81+
export function filter<T>(enumerable: ArrayLike<T>, conditionFn: (val: T) => boolean) {
82+
const filtered: T[] = []
83+
6184
forEach(enumerable, i => {
6285
if (conditionFn(i)) {
6386
filtered.push(i)
6487
}
6588
})
89+
6690
return filtered
6791
}
6892

6993
/**
7094
* @return {Integer} the number of items that are the same, starting from the 0th index, in a and b
7195
* @private
7296
*/
73-
function commonItemLength(listA, listB) {
97+
export function commonItemLength(listA: ArrayLike<unknown>, listB: ArrayLike<unknown>) {
7498
let offset = 0
99+
75100
while (offset < listA.length && offset < listB.length) {
76101
if (listA[offset] !== listB[offset]) {
77102
break
78103
}
79104
offset++
80105
}
106+
81107
return offset
82108
}
83109

84110
/**
85111
* @return {Array} the items that are the same, starting from the 0th index, in a and b
86112
* @private
87113
*/
88-
function commonItems(listA, listB) {
114+
export function commonItems<T>(listA: T[], listB: T[]): T[] {
89115
let offset = 0
116+
90117
while (offset < listA.length && offset < listB.length) {
91118
if (listA[offset] !== listB[offset]) {
92119
break
93120
}
94121
offset++
95122
}
123+
96124
return listA.slice(0, offset)
97125
}
98126

99127
// return new array without falsy items like ruby's `compact`
100-
function compact(enumerable) {
128+
export function compact<T>(enumerable: ArrayLike<T>) {
101129
return filter(enumerable, i => !!i)
102130
}
103131

104-
function reduce(enumerable, callback, initialValue) {
132+
export function reduce<T, U>(
133+
enumerable: ArrayLike<T>,
134+
callback: (prev: U, val: T, index: number) => U,
135+
initialValue: U
136+
): U {
105137
let previousValue = initialValue
138+
106139
forEach(enumerable, (val, index) => {
107140
previousValue = callback(previousValue, val, index)
108141
})
142+
109143
return previousValue
110144
}
111145

@@ -114,29 +148,34 @@ function reduce(enumerable, callback, initialValue) {
114148
* @return {Object} {key1:value1, key2:value2, ...}
115149
* @private
116150
*/
117-
function kvArrayToObject(array) {
118-
const obj = {}
151+
export function kvArrayToObject<T>(array: (T | string)[]): { [key: string]: T } {
152+
const obj: { [key: string]: T } = {}
153+
119154
for (let i = 0; i < array.length; i += 2) {
120155
let [key, value] = [array[i], array[i + 1]]
121-
obj[key] = value
156+
obj[key as string] = value as T
122157
}
158+
123159
return obj
124160
}
125161

126-
function objectToSortedKVArray(obj) {
127-
const keys = Object.keys(obj).sort()
128-
const result = []
162+
export function objectToSortedKVArray<T extends {}>(obj: T): (keyof T | T[keyof T])[] {
163+
const keys = Object.keys(obj).sort() as (keyof T)[]
164+
const result: (keyof T | T[keyof T])[] = []
165+
129166
keys.forEach(k => {
130167
result.push(k)
131168
result.push(obj[k])
132169
})
170+
133171
return result
134172
}
135173

136174
// check shallow equality of two non-nested arrays
137-
function isArrayEqual(arr1, arr2) {
138-
let l1 = arr1.length,
139-
l2 = arr2.length
175+
export function isArrayEqual<T>(arr1: ArrayLike<T>, arr2: ArrayLike<T>): boolean {
176+
let l1 = arr1.length
177+
let l2 = arr2.length
178+
140179
if (l1 !== l2) {
141180
return false
142181
}
@@ -146,42 +185,26 @@ function isArrayEqual(arr1, arr2) {
146185
return false
147186
}
148187
}
188+
149189
return true
150190
}
151191

152192
// return an object with only the valid keys
153-
function filterObject(object, validKeys = []) {
154-
let result = {}
193+
export function filterObject<T>(object: T, validKeys: string[] = []) {
194+
let result: { [key: string]: unknown } = {}
195+
155196
forEach(
156197
filter(Object.keys(object), key => validKeys.indexOf(key) !== -1),
157-
key => (result[key] = object[key])
198+
key => (result[key] = (object as any)[key])
158199
)
200+
159201
return result
160202
}
161203

162-
function contains(array, item) {
204+
export function contains<T>(array: T[], item: T): boolean {
163205
return array.indexOf(item) !== -1
164206
}
165207

166-
function values(object) {
167-
return Object.keys(object).map(key => object[key])
168-
}
169-
170-
export {
171-
detect,
172-
forEach,
173-
any,
174-
every,
175-
filter,
176-
commonItemLength,
177-
commonItems,
178-
compact,
179-
reduce,
180-
objectToSortedKVArray,
181-
kvArrayToObject,
182-
isArrayEqual,
183-
toArray,
184-
filterObject,
185-
contains,
186-
values,
208+
export function values<T extends {}>(object: T): T[keyof T][] {
209+
return (Object.keys(object) as (keyof T)[]).map(key => object[key])
187210
}
Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Keycodes from './keycodes'
22
import Keys from './keys'
3-
import { TAB } from 'mobiledoc-kit/utils/characters'
3+
import assert from './assert'
4+
import { TAB } from './characters'
45

56
/**
67
* @typedef Direction
@@ -12,7 +13,6 @@ export const DIRECTION = {
1213
FORWARD: 1,
1314
BACKWARD: -1,
1415
}
15-
import assert from './assert'
1616

1717
export const MODIFIERS = {
1818
META: 1, // also called "command" on OS X
@@ -21,11 +21,12 @@ export const MODIFIERS = {
2121
ALT: 8, // also called "option" on OS X
2222
}
2323

24-
export function modifierMask(event) {
24+
export function modifierMask(event: KeyboardEvent) {
2525
let { metaKey, shiftKey, ctrlKey, altKey } = event
26-
let modVal = (val, modifier) => {
26+
let modVal = (val: boolean, modifier: number) => {
2727
return (val && modifier) || 0
2828
}
29+
2930
return (
3031
modVal(metaKey, MODIFIERS.META) +
3132
modVal(shiftKey, MODIFIERS.SHIFT) +
@@ -52,12 +53,12 @@ const SPECIAL_KEYS = {
5253
DEL: Keycodes.DELETE,
5354
}
5455

55-
export function specialCharacterToCode(specialCharacter) {
56+
export function specialCharacterToCode(specialCharacter: keyof typeof SPECIAL_KEYS) {
5657
return SPECIAL_KEYS[specialCharacter]
5758
}
5859

5960
// heuristic for determining if `event` is a key event
60-
function isKeyEvent(event) {
61+
function isKeyEvent(event: Event) {
6162
return /^key/.test(event.type)
6263
}
6364

@@ -66,16 +67,22 @@ function isKeyEvent(event) {
6667
* that key listeners in the editor can use
6768
* to determine what sort of key was pressed
6869
*/
69-
const Key = class Key {
70-
constructor(event) {
70+
export default class Key {
71+
key: string
72+
keyCode: number
73+
charCode: number
74+
event: KeyboardEvent
75+
modifierMask: number
76+
77+
constructor(event: KeyboardEvent) {
7178
this.key = event.key
7279
this.keyCode = event.keyCode
7380
this.charCode = event.charCode
7481
this.event = event
7582
this.modifierMask = modifierMask(event)
7683
}
7784

78-
static fromEvent(event) {
85+
static fromEvent(event: KeyboardEvent) {
7986
assert('Must pass a Key event to Key.fromEvent', event && isKeyEvent(event))
8087
return new Key(event)
8188
}
@@ -92,12 +99,12 @@ const Key = class Key {
9299
return this.key
93100
}
94101

95-
isKey(identifier) {
102+
isKey(identifier: keyof typeof Keys) {
96103
if (this.isKeySupported()) {
97-
assert(`Must define Keys.${identifier}.`, Keys[identifier])
104+
assert(`Must define Keys.${identifier}.`, !!Keys[identifier])
98105
return this.key === Keys[identifier]
99106
} else {
100-
assert(`Must define Keycodes.${identifier}.`, Keycodes[identifier])
107+
assert(`Must define Keycodes.${identifier}.`, !!Keycodes[identifier])
101108
return this.keyCode === Keycodes[identifier]
102109
}
103110
}
@@ -242,7 +249,7 @@ const Key = class Key {
242249
return this.shiftKey
243250
}
244251

245-
hasModifier(modifier) {
252+
hasModifier(modifier: number) {
246253
return modifier & this.modifierMask
247254
}
248255

@@ -338,5 +345,3 @@ const Key = class Key {
338345
)
339346
}
340347
}
341-
342-
export default Key

0 commit comments

Comments
 (0)