10

I have two interfaces. They are really similar except for one key.

interface InitialStateFromDB {
  uploads: {
    companyImage: string,
    map: string
  },
  adminPasswords: string,
  postInfos: PostInfo[] | undefined
}

interface InitialState extends Omit<InitialStateFromDB, 'adminPasswords'> {
  adminPasswords: AdminPassword
}

And I get initialState from DB.

const initialStateFromDB: InitialStateFromDB = window.__PRELOADED_STATE__;

Then I extract value of each property from initialStateFromDB to make initialState.

let adminPasswords: AdminPassword = JSON.parse(initialStateFromDB.adminPasswords);
const initialState : InitialState = {
  uploads : initialStateFromDB.uploads,
  adminPasswords,
  postInfos: initialStateFromDB.postInfos
}

But I think there is a more simple way to merge except adminPasswords property. So I searched about this.

Clone a js object except for one key

But, I don't know how to do like that in typescript.

So, My question is that "How to clone object except for one key in Typescript"

Byeongin Yoon
  • 3,233
  • 6
  • 26
  • 44

3 Answers3

11

What you need is spread operator (check the "Object Spread and Rest" part). Typescript is a superset of JavaScript, both of them support this feature.

const {adminPasswords, ...state} = initialStateFromDB;
const initialState: InitialState = {
  ...state,
  adminPasswords: JSON.parse(adminPasswords)
}
Xie Guanglei
  • 418
  • 3
  • 7
  • 3
    this actually doesn't work if you want to omit the key completely, only works if you override it with something else. I haven't managed to do this in an elegant manner without casting. {...state, adminPasswords: undefined } does not match Omit – Hoffmann Sep 23 '19 at 08:35
  • 2
    Note that this creates a shallow clone, not a deep clone. – Atte Juvonen Jan 12 '21 at 17:48
2

I found one option that works if you want to omit the key entirely instead of overriding it - using ts object rest (ref documentation here):

In the case of the question asked, it'd be:

const {adminPasswords, ...initialState} : InitialState = {...initialStateFromDB};

The line above makes a copy of 'initialStateFromDB' excluding the 'adminPasswords' field. The new object created is 'initialState'.

0

Below code implements a small library (https://github.com/typestack/class-transformer), to transform plain object into a class object. Even you use JSON.parse you will have a plain JS object. So after transforming into a class object, password property is extracted from the object.

import { plainToInstance } from 'class-transformer';

export class UserDto{
constructor( public username: string, public password: string ) {}
}

getUser(){
    //const userDB = using your service, get user info from db,
    //then transform plain object retrieved from db into a class object
    const user = plainToInstance(UserDto, userDB); 
    const { password, ...user }: UserDto = { ...user[0] };
    return user;
}

You get user object (a shallow copy of type UserDto) without password field.

myteardrop4u
  • 31
  • 1
  • 1
  • 8