How to put typing in the return of Object.Keys being the types the Keys of the Object or the interface?

Asked

Viewed 32 times

0

I got the following

Interface

interface UserState {
  id: string;
  isDeleted: boolean;
  deletedAt: null | string;
  name: string;
  email: string;
  picture: string;
}

Object

const state: UserState  = {
  id: '',
  name: '',
  email: '',
  deletedAt: null,
  isDeleted: false,
  picture: ''
}

I am using vuex as central state lib and need to create a method (Mutation) that receives a payload (objeto: Partial<UserState>) and updates the state (objeto: UserState)

Attempt with spread Operator

// mutation    
SET_INFO (state: UserState, payload: Partial<UserState>) {
      state = {...state, ...payload}
}

The above example is not functional because this way vuex is not able to act reactively, perhaps because it overwrites the whole state.

Try with Object.Keys

  SET_INFO (state: UserState, payload: Partial<UserState>) {
    type UserStateKeys = keyof UserState

    const keyNames: UserStateKeys[] = Object.keys(state) as UserStateKeys[]
    keyNames.forEach((keyName) => {
        if (payload[keyName]) { 
          state[keyName] = payload[keyName]
        }
    })
  }

This one with pure Javascript is functional, but in typescript there is the following impeditive

Type 'string | Boolean | null | Undefined' is not Assignable to type 'Never'. Type 'Undefined' is not Assignable to type 'Never'. ts(2322) (Parameter) keyName: keyof Userstate

From what I understand of the message, when I use the keyName as index no payload, cannot accurately determine the type of value returned to state[keyName].

Line of error message state[keyName] = payload[keyName]

So much so that if I do it here is accepted state.email = payload[keyName] as string

Is there any way I can do this typing?

  • 1

    Instead of using the Operator spread to do the merge properties, use the function Object.assign, which has semantics of set (what works for the way Vuex treats the state). See more on What is the difference between Object.assign and spread Operator?.

  • Maybe it’s worth [Dit] the question to make it a little wider (the Vue part doesn’t have much to do with the error you’re actually having with Typescript).

  • Good, as I agree with Vue having nothing to do with the typing problem. But I put it to contextualize where the code will be executed, in the expectation that someone would bring a solution according to this situation in vuex Mutation.

1 answer

3

In this case the compiler of typescript cannot infer whether the types are the same, since a union of different types has no order, the solution is to leave some of the typing aside in this case.

  SET_INFO (state: UserState, payload: Partial<UserState>) {
    const keyNames = Object.keys(state)
    keyNames.forEach((keyName) => {
        if (payload[keyName]) { 
          state[keyName] = payload[keyName]
        }
    })
  }
  • 2

    Just for the record, regarding Typescript, there’s a discussion about it on Github.

  • Actually this Issue is not related to this problem, in this case it is more related to the limitations of typescript. Never is used when you know something will never occur and in this case typescript is correct in assuming this, because it cannot relate the if block to the assignment that is occurring.

  • 1

    Yes, @Ayayem, exactly. The limitation of Typescript (which is discussed on the Issue in question) leads to problems like this.

  • @Ayayem In this case I cannot remove the typing in this method, it belongs to an object that infers types.

  • I had forgotten to remove a cast in the example, sorry.

  • @Ayayem const mutations: MutationTree<UserState> = {SET_INFO(state, paylod) {...} }

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.