Skip to content

Commit ccec27b

Browse files
committed
Add type-safety to IDB keys
1 parent d913df0 commit ccec27b

File tree

2 files changed

+125
-15
lines changed

2 files changed

+125
-15
lines changed

src/main/scala/org/scalajs/dom/IDBTypes.scala

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,45 @@
99
*/
1010
package org.scalajs.dom
1111

12+
import scala.annotation.nowarn
13+
import scala.language.implicitConversions
1214
import scala.scalajs.js
1315
import scala.scalajs.js.annotation._
16+
import scala.scalajs.js.typedarray._
1417
import scala.scalajs.js.|
1518

19+
/** number, date, string, binary, or array.
20+
*
21+
* Array objects, where every item is defined, is itself a valid key, and does not directly or indirectly contain itself. This includes empty arrays. Arrays can contain other arrays.
22+
*
23+
* https://w3c.github.io/IndexedDB/#key
24+
* https://github.com/web-platform-tests/wpt/blob/master/IndexedDB/idb-binary-key-roundtrip.htm
25+
*/
26+
@js.native
27+
sealed trait IDBKey extends js.Any
28+
29+
@nowarn("cat=unused")
30+
object IDBKey {
31+
32+
type Flat = Double | js.Date | String | ArrayBuffer | ArrayBufferView | BigInt64Array | BigUint64Array | DataView | Float32Array | Float64Array | Int16Array | Int32Array | Int8Array | Uint16Array | Uint32Array | Uint8Array | Uint8ClampedArray
33+
34+
sealed trait IsKey[-A]
35+
object IsKey {
36+
@inline implicit def flat[A](implicit e: |.Evidence[A, Flat]): IsKey[A] = null
37+
@inline implicit def array[A](implicit e: IsKey[A]): IsKey[js.Array[A]] = null
38+
@inline implicit def coproduct[A, B](implicit a: IsKey[A], b: IsKey[B]): IsKey[A | B] = null
39+
}
40+
41+
@inline implicit def from[A: IsKey](a: A): IDBKey =
42+
a.asInstanceOf[IDBKey]
43+
44+
@inline implicit def undef[A: IsKey](a: A): js.UndefOr[IDBKey] =
45+
a.asInstanceOf[js.UndefOr[IDBKey]]
46+
47+
@inline implicit def undefOrLeft[A: IsKey, B](a: A): js.UndefOr[IDBKey | B] =
48+
a.asInstanceOf[js.UndefOr[IDBKey | B]]
49+
}
50+
1651
/**
1752
* IndexedDB transaction mode
1853
* Provides constants for IDB Transaction modes
@@ -85,7 +120,7 @@ class IDBObjectStore extends js.Object {
85120
*/
86121
def keyPath: String = js.native
87122

88-
def count(key: js.Any = js.native): IDBRequest = js.native
123+
def count(key: IDBKey = js.native): IDBRequest = js.native
89124

90125
/**
91126
* To determine if the add operation has completed successfully, listen for the
@@ -96,7 +131,7 @@ class IDBObjectStore extends js.Object {
96131
*
97132
* MDN
98133
*/
99-
def add(value: js.Any, key: js.Any = js.native): IDBRequest = js.native
134+
def add(value: js.Any, key: IDBKey = js.native): IDBRequest = js.native
100135

101136
/**
102137
* Clearing an object store consists of removing all records from the object store and
@@ -122,15 +157,15 @@ class IDBObjectStore extends js.Object {
122157
*
123158
* MDN
124159
*/
125-
def put(value: js.Any, key: js.Any = js.native): IDBRequest = js.native
160+
def put(value: js.Any, key: IDBKey = js.native): IDBRequest = js.native
126161

127162
/**
128163
* The method sets the position of the cursor to the appropriate record,
129164
* based on the specified direction.
130165
*
131166
* MDN
132167
*/
133-
def openCursor(range: js.UndefOr[IDBKeyRange | js.Any] = js.undefined,
168+
def openCursor(range: js.UndefOr[IDBKey | IDBKeyRange] = js.undefined,
134169
direction: js.UndefOr[
135170
IDBCursorDirection] = js.undefined): IDBRequest = js.native
136171

@@ -140,7 +175,7 @@ class IDBObjectStore extends js.Object {
140175
*
141176
* MDN
142177
*/
143-
def openKeyCursor(range: js.UndefOr[IDBKeyRange | js.Any] = js.undefined,
178+
def openKeyCursor(range: js.UndefOr[IDBKey | IDBKeyRange] = js.undefined,
144179
direction: js.UndefOr[
145180
IDBCursorDirection] = js.undefined): IDBRequest = js.native
146181

@@ -166,15 +201,15 @@ class IDBObjectStore extends js.Object {
166201
*
167202
* MDN
168203
*/
169-
def get(key: js.Any): IDBRequest = js.native
204+
def get(key: IDBKey): IDBRequest = js.native
170205

171206
/**
172207
* If a value is successfully found, then a structured clone of it is created and set as
173208
* the result of the request object.
174209
*
175210
* MDN
176211
*/
177-
def getAll(query: js.UndefOr[IDBKeyRange | js.Any] = js.undefined,
212+
def getAll(query: js.UndefOr[IDBKey | IDBKeyRange] = js.undefined,
178213
count: js.UndefOr[Int] = js.undefined): IDBRequest = js.native
179214

180215
/**
@@ -183,7 +218,7 @@ class IDBObjectStore extends js.Object {
183218
*
184219
* MDN
185220
*/
186-
def getAllKeys(query: js.UndefOr[IDBKeyRange | js.Any] = js.undefined,
221+
def getAllKeys(query: js.UndefOr[IDBKey | IDBKeyRange] = js.undefined,
187222
count: js.UndefOr[Int] = js.undefined): IDBRequest = js.native
188223

189224
/**
@@ -192,15 +227,15 @@ class IDBObjectStore extends js.Object {
192227
*
193228
* MDN
194229
*/
195-
def getKey(key: js.Any): IDBRequest = js.native
230+
def getKey(key: IDBKey): IDBRequest = js.native
196231

197232
/**
198233
* returns an IDBRequest object, and, in a separate thread, deletes the current
199234
* object store.
200235
*
201236
* MDN
202237
*/
203-
def delete(key: js.Any): IDBRequest = js.native
238+
def delete(key: IDBKey): IDBRequest = js.native
204239
}
205240

206241
trait IDBVersionChangeEventInit extends EventInit {
@@ -286,7 +321,7 @@ class IDBIndex extends js.Object {
286321
*/
287322
def objectStore: IDBObjectStore = js.native
288323

289-
def count(key: js.Any): IDBRequest = js.native
324+
def count(key: IDBKey): IDBRequest = js.native
290325

291326
/**
292327
* If you want to see how many records are between keys 1000 and 2000 in an object store,
@@ -302,7 +337,7 @@ class IDBIndex extends js.Object {
302337
*
303338
* MDN
304339
*/
305-
def getKey(key: js.Any): IDBRequest = js.native
340+
def getKey(key: IDBKey): IDBRequest = js.native
306341

307342
/**
308343
* Returns an IDBRequest object, and, in a separate thread, creates a cursor over the
@@ -320,7 +355,7 @@ class IDBIndex extends js.Object {
320355
*
321356
* MDN
322357
*/
323-
def get(key: js.Any): IDBRequest = js.native
358+
def get(key: IDBKey): IDBRequest = js.native
324359

325360
/**
326361
* The method sets the position of the cursor to the appropriate record, based on the
@@ -371,7 +406,7 @@ class IDBCursor extends js.Object {
371406
*
372407
* MDN
373408
*/
374-
def key: js.Any = js.native
409+
def key: IDBKey = js.native
375410

376411
/**
377412
* Returns the cursor's current effective key. If the cursor is currently being
@@ -397,7 +432,7 @@ class IDBCursor extends js.Object {
397432
*
398433
* W3C
399434
*/
400-
def continue(key: js.Any = ???): Unit = js.native
435+
def continue(key: IDBKey = ???): Unit = js.native
401436

402437
/**
403438
* Returns an IDBRequest object, and, in a separate thread, deletes the record at the
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package org.scalajs.dom.tests.shared
2+
3+
import org.scalajs.dom._
4+
import scala.scalajs.js
5+
import scala.scalajs.js.|
6+
7+
trait CompilationTest {
8+
9+
trait IDBKeyTests {
10+
def o: IDBObjectStore
11+
12+
locally {
13+
import IDBKey._
14+
15+
implicitly[IsKey[Double]]
16+
implicitly[IsKey[Int]]
17+
implicitly[IsKey[Int | String]]
18+
19+
implicitly[IsKey[js.Array[Double]]]
20+
implicitly[IsKey[js.Array[Int]]]
21+
implicitly[IsKey[js.Array[Int | String]]]
22+
23+
implicitly[IsKey[String | js.Array[Int]]]
24+
implicitly[IsKey[String | js.Array[Int] | Int]]
25+
implicitly[IsKey[js.Array[String] | js.Array[Int] | Int]]
26+
27+
implicitly[IsKey[js.Array[js.Array[Double]]]]
28+
implicitly[IsKey[js.Array[js.Array[Int]]]]
29+
implicitly[IsKey[js.Array[String | js.Array[Int]]]]
30+
implicitly[IsKey[js.Array[String | js.Array[Int] | js.Array[String | Double]]]]
31+
}
32+
33+
o.get(123)
34+
o.get(123.0)
35+
o.get("qwe")
36+
o.get(null.asInstanceOf[String | Double])
37+
o.get(js.Array[Int]())
38+
o.get(js.Array[Double]())
39+
o.get(js.Array[String | Double]())
40+
o.get(js.Array[js.Array[Double]]())
41+
o.get(js.Array[js.Array[String | Double]]())
42+
o.get(js.Array[String | js.Array[String | Double]]())
43+
o.get(js.Array[js.Array[String | Double] | String]())
44+
o.get(js.Array[String | js.Array[String | Double] | String]())
45+
o.get(js.Array[js.Array[js.Date | Double] | js.Array[String | Double] | js.Array[String | js.Date]]())
46+
47+
o.getAll(123)
48+
o.getAll(123.0)
49+
o.getAll("qwe")
50+
o.getAll(null.asInstanceOf[String | Double])
51+
o.getAll(js.Array[Int]())
52+
o.getAll(js.Array[Double]())
53+
o.getAll(js.Array[String | Double]())
54+
o.getAll(js.Array[js.Array[Double]]())
55+
o.getAll(js.Array[js.Array[String | Double]]())
56+
o.getAll(js.Array[String | js.Array[String | Double]]())
57+
o.getAll(js.Array[js.Array[String | Double] | String]())
58+
o.getAll(js.Array[String | js.Array[String | Double] | String]())
59+
o.getAll(js.Array[js.Array[js.Date | Double] | js.Array[String | Double] | js.Array[String | js.Date]]())
60+
61+
123: js.UndefOr[IDBKey]
62+
123.0: js.UndefOr[IDBKey]
63+
"qwe": js.UndefOr[IDBKey]
64+
null.asInstanceOf[String | Double]: js.UndefOr[IDBKey]
65+
js.Array[Int](): js.UndefOr[IDBKey]
66+
js.Array[Double](): js.UndefOr[IDBKey]
67+
js.Array[String | Double](): js.UndefOr[IDBKey]
68+
js.Array[js.Array[Double]](): js.UndefOr[IDBKey]
69+
js.Array[js.Array[String | Double]](): js.UndefOr[IDBKey]
70+
js.Array[String | js.Array[String | Double]](): js.UndefOr[IDBKey]
71+
js.Array[js.Array[String | Double] | String](): js.UndefOr[IDBKey]
72+
js.Array[String | js.Array[String | Double] | String](): js.UndefOr[IDBKey]
73+
js.Array[js.Array[js.Date | Double] | js.Array[String | Double] | js.Array[String | js.Date]](): js.UndefOr[IDBKey]
74+
}
75+
}

0 commit comments

Comments
 (0)