【问题标题】:Is there an other way to remove a property from an object than with "delete"?除了“删除”之外,还有其他方法可以从对象中删除属性吗?
【发布时间】:2019-05-08 10:58:37
【问题描述】:

我对 Javascript 或 Typescript 中的 delete 关键字的行为有疑问。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete

我需要的是一种从对象中选择属性的方法和一种从对象中省略属性的方法。

Typescript 带有一个内置类型 Pick https://www.typescriptlang.org/docs/handbook/advanced-types.html

type Pick<T, K extends keyof T> = { [P in K]: T[P]; }

Pick的反义词是Omit,可以这样实现:

export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof 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 omitMany = <T, K extends keyof T>(entity: T, props: K[]): 
      Omit<T, K> => {
  return props.reduce((s, prop) => (delete s[prop] ,s), entity)
 }

对于省略,我使用了 delete 关键字,一开始它似乎可以工作,但现在我遇到了一些问题。主要问题是omitMany 的原始对象被修改了。这会导致在我的程序中保留原始数据和保留状态的问题。

我写了一个简单的例子来说明我的问题:

// First I make some interface that contains some structure for data
interface SomeObject { x: string, y: number, z: boolean }

// Initialize object1 with properties x, y and z, containing the important data
// I want to preserve all the data in object1 throug the entire program
let object1: SomeObject = { x: "something", y: 0, z: false }
// I can print all properties of object1
console.log(`Object 1: x = ${object1.x}, y = ${object1.y}, z = ${object1.z}`) 
// OUTPUT: "Object 1: x = something, y = 0, z = false"

// omit or delete property 'x' from object 1, defining object 2
let object2 = omitMany(object1, ["x"]) // The type of object2 is: {y: number, z: boolean}
// Here I can only get properties z and y, because x has been omited
// Calling: object2.x gives an compile error
console.log(`Object 2: y = ${object2.y}, z = ${object2.z}`)
// OUTPUT: Object 2: y = 0, z = false (as expected)

// Everything works fine from here, but...
// When I recall omitMany on object1 the following happens: 

// Initialize object3 from object1, removing 'x' from an object where x = undefined
let object3 = omitMany(object1, ["x"]) // This code compiles, omiting 'x' from object2 gives an compiler error
//Printing object3 does show no problems, since it satisfies the expected result. Remove 'x' and keep 'y' and 'z'
console.log(`Object 3: y = ${object3.y}, z = ${object3.z}`)
// OUTPUT: Object 3: y = 0, z = false

// But when I print object1 again
console.log(`Object 1: x = ${object1.x}, y = ${object1.y}, z = ${object1.z}`) 
// OUTPUT: Object 1: x = undefined, y = 0, z = false 
// We lost the data of 'x'!!!


// I also ran into problems when I try to pick property 'x' from the original object1 
let object4 = pickMany(object1, ["x"]) // The type of object4 is {x: string}
// When I print 'x' from object4 it is still undefined
console.log(`Object 4: x = ${object4.x}`) 
// OUTPUT: Object 4: x = undefined

我知道这与delete 的行为有关,但是还有其他方法可以在不丢失原始对象信息的情况下从对象中删除属性吗?因此,保留所有值和属性。

这个问题可以用一个临时变量来解决,但是我首先想看看有没有其他的解决方案。

【问题讨论】:

    标签: javascript typescript properties preserve


    【解决方案1】:

    这就是我解决这个问题的方法:

    // https://stackoverflow.com/a/49579497/14357
    /** Extracts optional keys from T */
    export type OptionalKeys<T> = {
        [K in keyof T]-?: ({} extends {
            [P in K]: T[K];
        } ? K : never);
    }[keyof T];
    
    /** Typesafe way to delete optional properties from an object using magic of OptionalKeys<T> */
    export const deleteOptionalProperty = <T>(obj: T, id: OptionalKeys<T>): T => {
        const { [id]: deleted, ...newState } = obj;
        return newState as T // this type-conversion is safe because we're sure we only deleted optional props
    }
    
    export const deleteOptionalProperties = <T>(obj: T, ...ids: OptionalKeys<T>[]): T =>
        ids.reduce((prev, id) => deleteOptionalProperty(prev, id), obj)
    

    【讨论】:

    • 使用never 似乎是一个很好的解决方案,never 类型的属性与已删除的属性几乎相同
    • 但是如何使用这个,OptionalKeys&lt;SomeObject&gt; 的类型是never。那么我怎样才能给id 参数提供密钥呢?
    • @LivingLife 明白了。这种方法只适用于删除 optional 键(即{someValue?:string}),这样可以保证能够返回与输入类型相同的类型。如果我以后有时间,我会制作一个更宽松的版本。
    • 我没有使用过type OptionalKeys jet,但您的回答确实包含了一些有用的部分。我重构了我的omitMany()omitOne() 方法以利用这种删除属性const { [id]: deleted, ...newState } = obj; 的方式,然后返回newState。这是删除属性的一种巧妙方法,因为您创建了一个新对象,其中[id] 属性设置为deleted(值无关紧要)然后newState 将具有除id 之外的所有剩余属性.这现在解决了我的问题,因为我有另一种删除属性的方法,而不是 delete 关键字
    猜你喜欢
    • 2018-03-01
    • 2015-12-08
    • 2019-09-14
    • 2017-07-18
    • 2020-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-24
    相关资源
    最近更新 更多