user11534547
user11534547

Reputation:

How to Omit "DELETE" many properties from an Object?

I have two methods that return the following types Pick<T, K> and Omit<T, K> where Omit is type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>. I have some trouble when it comes to removing multiple properties from an object.

I have a method pickOne that selects one property from an object, a method pickMany that picks multiple properties from an object and a method omitOne that removes one property from an Object. I would like to have a OmitMany method to remove multiple properties from an Object, but I got stuck when fixing the type errors in the method.

Implementation of the methods:

export let pickOne = <T, K extends keyof T>(entity: T, props: K ): Pick<T, K> => {
    return { [props] : entity[props] } as Pick<T, K>
}

export let pickMany = <T, K extends keyof T>(entity: T, props: K[]) => {
   return props.reduce((s, prop) => (s[prop] = entity[prop], s) , {} as Pick<T, K>)
}

export let omitOne = <T, K extends keyof T>(entity: T, prop: K): Omit<T, K> => {
    const { [prop]: deleted, ...newState} = entity
    return newState
}

// And the OmitMany for so far I tried, the problem is with storing the entity
// in a temporary variable. This function only omits the last property in the
// the array. I would like an implementation simular to pickMany.
export let omitMany = <T, K extends keyof T>(entity: T, props: K[]): Omit<T, K> => {
    let result = entity as Omit<T, K>
    props.forEach(prop => {
        result = omitOne(entity, prop)
    })
    return result
}

I expect the output of omitMany({x: 1, y: 2, z: 3, r: 4}, ['x', 'y']) to be an object of type {z: number, r: number}, but right know the output is an object of type {x: number, z: number, r: number}

Upvotes: 3

Views: 1469

Answers (2)

CaringDev
CaringDev

Reputation: 8551

Since TS 3.5 and the standard Omit type:

function omit<T, K extends keyof T>(obj: T, ...props: K[]): Omit<T, K> {
    return props.reduce((o, k) => { delete o[k]; return o; }, { ...obj });
}

Upvotes: 2

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249646

Your issue has little to do with typescript. The types work out as you expect them to for omitMany, the only problem is that at run-time it does not remove all the properties you expect it to and this is cause by the fact that you call omitOne on entity instead of the previous result. This requires some type assertions unfortunately, but it will work:

type Omit<T, K> = Pick<T, Exclude<keyof T, K>>

let omitOne = <T, K extends keyof T>(entity: T, prop: K): Omit<T, K> => {
    const { [prop]: deleted, ...newState } = entity
    return newState
}

let omitMany = <T, K extends keyof T>(entity: T, props: K[]): Omit<T, K> => {
    let result = entity as Omit<T, K>
    props.forEach(prop => {
        result = omitOne(result, prop as unknown as keyof Omit<T, K>) as Omit<T, K>
    })
    return result
}

let o = omitMany({ x: 1, y: 2, z: 3, r: 4 }, ['x', 'y'])
console.log(o)

Playground

Upvotes: 1

Related Questions