0

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

David Barker
  • 14,484
  • 3
  • 48
  • 77

1 Answers1

2

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]>

Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
  • 1
    You'll also need to introduce generic parameter in inner function, otherwise `typeof parts[number]` won't work https://www.typescriptlang.org/play?jsx=0#code/MYewdgzgLgBA7gSygCxAV1gXhgHgCoB8AFCAEYBWApsFAFwx4CUMmBuA0pQJ4QyUAeUSmAAmvAIIAnSQEMuOANbcQAMwYFiAOm0AHGZKgR6nHo3oB5ALZJ8AGhhQuOyqph6DEANpg0l0pUkAXTZWGABvACgYaJhJSig0STAYInMKaihNJR4SdJpmGQlpOUVlNUJGTTiRNGBKIiIZYGB7bOZQyJiumAQ1IgBCd0NNBDBgABs0EUoIIjbmTu6lpuBPbMCWGDIqGjXuQIBuKKWYAF9jpbiEpJgVo67T+zDT294mI9OjiNBIWG3NsK3egARnspHoACZ7MB6ABmM5HH7QWIzNDjLDwJCoDC5ciMIgAchkBPsBOABMYByAA – Aleksey L. Dec 21 '20 at 12:19
  • @TitianCernicova-Dragomir this makes sense and finally understanding why `Object.keys` returns a `string[]` is invaluable, thank you! – David Barker Dec 21 '20 at 13:00