Record And NumericKeyRecord
This page discusses how to use NumericRecord
and Record
.
Type Checks
Record
is Typescript native utility, we don't need to import it.NumericKeyRecord<T>
is Record<${number}
, T> under the hood.With
Record<string, T>
, the key must be non-numeric.With
NumericKeyRecord<T>
, the key must be numeric string with one exception: it can be number if used as object key because JS object coerce number key into string key.With
NumericKeyRecord<T>
, write operations acceptT[]
as values.
import {
MetaTypeCreator,
createRef,
getDatabase,
push,
set,
update,
NumericKeyRecord,
} from 'firesagejs'
type Example = MetaTypeCreator<{
a: NumericKeyRecord<{ c: number }>
b: Record<string, { c: number }>
}>
const exampleRef = createRef<Example>(getDatabase())
set(exampleRef('a'), { 123: { c: 999 } }) // ok, key is numeric
set(exampleRef('a'), [{ c: 999 }, { c: 111 }]) // ok, accept array, store in db as { 0:{ c: 999 }, 1:{ c: 111 } }
set(exampleRef('b'), { abc: { c: 999 } }) // ok, key is non-numeric
update(exampleRef('a'), ['123'], [{ c: 999 }]) // ok, key is numeric
update(exampleRef('b'), ['a1b2c3'], [{ c: 999 }]) // ok, key is non-numeric
update(
exampleRef(),
['a', 'b'],
[[{ c: 999 }, { c: 111 }], { abc: { c: 999 }, efg: { c: 111 } }]
) // ok, accept array, store in db as { a: { 0: { c: 999 }, 1: { c: 111 } }, b: { abc: { c: 999 }, efg: { c: 111 } }
set(exampleRef('a'), { a1b2c3: { c: 999 } }) // not ok, expect numeric key
set(exampleRef('b'), { 123: { c: 999 } }) // not ok, expect non-numeric key
update(exampleRef('a'), ['abc'], [{ c: 999 }]) // not ok, expect numeric key
update(exampleRef('b'), ['123'], [{ c: 999 }]) // not ok, expect non-numeric key
push(exampleRef('a'), { 123: { c: 999 } }) // not ok, cannot push NumericKeyRecord<T>
push(exampleRef('b'), { abc: { c: 999 } }) // not ok, cannot push Record<string, T>
update(exampleRef('a'), [123], [{ c: 999 }]) // not ok, is number but expect numeric string
// Record<`${number}`, T> and Record<number, T> is invalid type.
// use NumericKeyRecord<T> instead
type Example2 = MetaTypeCreator<{
a: Record<`${number}`, { c: number }>
b: Record<number, { c: number }>
}>
Understanding Error Messages
Due to complexity, the error suggestion for incorrect path check and numeric key check is generic, will give this deeper thoughts in the futrue.
Due to complexity, the error suggestion for incorrect path check and numeric key check is generic, will give this deeper thoughts in the futrue.
Never Read As Array
According to this blog, we can:
- Write data as array
- Possibly Read data as array
FireSageJS keeps the write data as array
functionality, because it is convenient and has no drawbacks.
This is not true regarding possibly read data as array
. Althought the Firebase team thinks this is a good idea, it introduces extra complexity: developers need to check the data type on runtime to see if it is an array or an object.
There are 2 ways to solve this:
If it is an object, convert it to an array, else do nothing.
If it is an array, convert it to an object, else do nothing. <-- FireSageJS implements this.
FireSageJS implements the 2nd approach and stays away from the 1st approach because converting an object to an array results in length
value that is not meaningful.
For example, if we read {2: 'c', 4: 'e'}
and convert it to an array would end up with [,,'c',,'e']
and the length is 5
, giving us the impression that we have 5
elements.
But in fact, there are only 2
elements that really count; the rest are just empty
elements.
Meanwhile, converting an array to object is safer. Take { 0: 'a', 2: 'c', 4: 'e' }
, it is read as ['a', undefined, 'c', undefined, 'e']
and FireSageJS converts it back to { 0: 'a', 2: 'c', 4: 'e' }
.
You may think that we lost some data because we discarded undefined
, but actually we didn't.
This is because undefined
is not a valid RTDB data type. We can read something as undefined
, but we are never able to write something as undefined
.
Now we know that undefined
is safe to discard, what about length? Object.keys({ 0: 'a', 2: 'c', 4: 'e' }).length
returns 3
and we do have 3
meaningful data points.
FireSageJS never reads data as an array, you always get an object or primitive data type.