Reputation: 31495
I have some objects containing DB documents that I constantly need to convert to arrays.
Example:
const MY_OBJECT = {
docId_1: {...doc1},
docId_2: {...doc2},
docId_3: {...doc3},
// AND SO ON
}
I need to convert it to an array like this:
const MY_ARRAY_FROM_OBJECT = [
{...doc1},
{...doc2},
{...doc3},
// AND SO ON...
]
And this is code I use to do the conversion:
function buildArrayFromObject(obj) {
const keys = Object.keys(obj);
const arr = [];
for (const key of keys) {
arr.push(obj[key]);
}
return arr;
}
And I need to type that function so I can use it with objects that has types like these:
interface BLOGPOSTS_ALL {
[key: string]: BLOGPOST
}
interface PRODUCTS_ALL {
[key: string]: PRODUCT
}
So when I call them with each different object, I want Typescript to know what the return array type will be.
For example:
const BLOGPOSTS_ALL_ARRAY = buildArrayFromObject(BLOGPOSTS_ALL); // SHOULD BE "BLOGPOST[]"
const PRODUCTS_ALL_ARRAY = buildArrayFromObject(PRODUCTS_ALL); // SHOULD BE "PRODUCT[]"
Here is what I've tried:
function buildArrayFromObject<T, K extends keyof T>(obj: T): T[K][] {
const keys = Object.keys(obj);
const arr = [];
for (const key of keys) {
arr.push(obj[key]);
}
return arr;
}
But I'm getting this error:
And the returning type of the function is being evaluated by Typescript as type never[]
Upvotes: 0
Views: 1034
Reputation: 1926
What you could do is the following:
Create an abstraction type to define what your database handles look like:
// Create an Entity type to define how your database objects look like
type Entities<T> = { [key: string]: T | undefined }
Therefore you can express your products and blogposts like this:
type BLOGPOSTS = Entities<BLOGPOST>;
type PRODUCTS = Entities<PRODUCT>;
In order to convert them in type-safe manner into an array you can use the Object.values
method provided by the JavaScript API - for more information please see MDN
It's possible to replace the method buildArrayFromObject
by something like this:
const isNil = <T>(a: T | null | undefined): a is null | undefined => a === null || a === undefined;
const isAssigned = <T>(a: T | null | undefined): a is T => !isNil(a);
const entitiesToArray = <T>(entity: Entities<T>): T[] => Object.values(entity).filter(isAssigned);
This method uses the Object.values
method to convert the object into an array. Afterwards it get's filtered to contain an array without undefined values. Therefore I made use of two helper methods isAssigned
and isNil
. Please see this CodeSandbox for an example: Code SandBox
Based on your concern, that the Object.value method is not supported by IE11, you can add a polyfill for that one. Either by pasting a polyfill in or by adding it using npm to your project. Alternatively you can replace this code by the following answer Use Object.keys to mimick Object.values
Upvotes: 1