Search code examples
typescriptkeyof

Type 'any' is not assignable to type 'never' when trying to set Object property with variable which is keyof ObjectType


In the code below typscript compiler shows error in update method, saying that 'any' is not assignable to type 'never'. I noticed that keyof type not working when the type contains boolean mixed with other types. How can I make it compile having mixed type values in the type?

type ConfigState = {
  isAdminSet: boolean;
  isDatabaseConnected: boolean;
  adminName: string;
};

export class ConfigManager {
  state: ConfigState = {
    isAdminSet: false,
    isDatabaseConnected: false,
    adminName: "",
  };

  update(key: keyof ConfigState, value: ConfigState[keyof ConfigState]) {
    this.state[key] = value;
  }
}

But this compiles:

type ConfigState = {
  isAdminSet: boolean;
  isDatabaseConnected: boolean;
};

export class ConfigManager {
  state: ConfigState = {
    isAdminSet: false,
    isDatabaseConnected: false,
  };

  update(key: keyof ConfigState, value: ConfigState[keyof ConfigState]) {
    this.state[key] = value;
  }
}

Solution

  • TLDR: TypeScript doesn't know if your value will fit into the chosen property of state.

    In your first example, all the properties are boolean, so any is inferred as a boolean. But as soon as you add an other type (here, a string), any can't be inferred without restricting the key. Hence, it is inferred as never, and you can't assign any to never.

    In this case, you must (I think) use a generic for this. Moreover, this will ensure type safety.

    Take a look at this section of TS documentation: https://www.typescriptlang.org/docs/handbook/2/generics.html#using-type-parameters-in-generic-constraints

    type ConfigState = {
      isAdminSet: boolean;
      isDatabaseConnected: boolean;
      adminName: string;
    };
    
    export class ConfigManager {
      state: ConfigState = {
        isAdminSet: false,
        isDatabaseConnected: false,
        adminName: "",
      };
    
      update<Key extends keyof ConfigState>(key: Key, value: ConfigState[Key]) {
        this.state[key] = value;
      }
    }