I'd like to type a generic function to only include values that are of type object (and ignore/disallow the other ones).
This is to only allow updating objects within a map. Is it possible? I haven't been able to find a solution.
enum SKey {
key1 = "key1",
key2 = "key2",
}
type SItem = {
[SKey.key1]: boolean;
[SKey.key2]: {
attr1: string;
attr2: boolean;
};
};
const mapDB: SItem = {
[SKey.key1]: true,
[SKey.key2]: {
attr1: "hello",
attr2: true,
},
};
export function updateDbItem<K extends keyof SItem>(
key: K,
newFields: Partial<SItem[K]>
) {
const item: SItem[K] = mapDB[key];
mapDB[key] = { ...item, ...newFields };
}
But obviously I get the following error
error TS2698: Spread types may only be created from object types.
mapDB[key] = { ...item, ...newFields };
~~~~~~~~
Thank you for your help!
You can use a mapped type to derive a union of only the keys whose values are object types.
In the code you've shown, this evaluates to only
SKey.key2
, but if you add more entries toSItem
, then it will also include other keys that meet the criteria.
Here's an example:
type SItemObjectKey = keyof {
[
K in keyof SItem as SItem[K] extends Record<string, unknown>
? K
: never
]: unknown;
};
export function updateDbItem<K extends SItemObjectKey>(
key: K,
newFields: Partial<SItem[K]>
) {
const item: SItem[K] = mapDB[key];
mapDB[key] = { ...item, ...newFields }; // Ok now!
}
See also: the utility type Record<Keys, Type>