David Barker
David Barker

Reputation: 14620

Typescript: remove keys from generic object

I'm currently attempting to write a higher order function without that will return a new object without the keys passed into the returned function.

I can't see a way to get the key in the reducer to be of keyof T as Object.keys returns a string[] not Array<keyof T>, if it did I think it would solve this. Am I missing something obvious with the types here that would solve this?

const without = <T>(object: T) => (...parts: Array<keyof T>) => {
    return Object.keys(object).reduce((acc, key) => {
        // `key` is of type `string` not `keyof T`
        if (!parts.includes(key)) {
            acc[key] = object[key];
        }
        return acc;
    }, {} as Omit<T, typeof parts[number]>);
};

const obj = { a: 1, b: 2, c: 3 };
const result = without(obj)('a', 'c');

TS Playground

Upvotes: 0

Views: 1926

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249486

Object.keys will return string[] for reasons outlined here. You will need to use type assertions to make this work.

const without = <T>(object: T) => <K extends keyof T>(...parts: Array<K>): Omit<T, K> => {
    return (Object.keys(object) as Array<keyof T>).reduce((acc, key) => {
        if (!parts.includes(key as any)) {
            acc[key] = object[key];
        }
        return acc;
    }, {} as T);
};

const obj = { a: 1, b: 2, c: 3 };
const result = without(obj)('a', 'c');

Playground Link

Beside the assertion to Array<keyof T> I also changed the type of the initial value of reduce to T. This is order to allow the assignment acc[key] = object[key]. T[keyof T] will be assignable to itself, but Omit<T, typeof parts[number]>[keyof T] will not be assignable. T will then in turn be assignable to Omit<T, typeof parts[number]>

Upvotes: 2

Related Questions