Record And NumericKeyRecord
This page discusses how to use NumericRecord and Record.
Type Checks
Recordis 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 }>
}>
how to use `NumericRecord` and `RecordUnderstanding Error Messages
expect numeric key
expect non-numeric keyDue to complexity, the error suggestion for incorrect path check and numeric key check is generic, will give this deeper thoughts in the futrue.
expect numeric key
expect non-numeric key
cannot push NumericKeyRecord<T>
cannot push Record<string, T>Due to complexity, the error suggestion for incorrect path check and numeric key check is generic, will give this deeper thoughts in the futrue.
received number but expect numeric stringNever Read As Array
According to this blog, we can:
- Write data as array
store data as array- Possibly Read data as array
possibly read data as arrayFireSageJS 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.