Home > Software engineering >  Assigning to an object literal dynamically in Typescript
Assigning to an object literal dynamically in Typescript

Time:01-03

I have been at this for hours and cannot determine why I am getting this typescript error.

I can't show the actual code, but I've reduced the problem down to the following example code:

    abstract class CustomerType {
        protected abstract customerInfo?: IUserInfo;
    }
    
    enum CustomerGroup {
        FirstTimer = "first timer",
        Subscriber = "subscriber"
    }
    
    interface IUserInfo {
        group?: CustomerGroup;
        firstClassMember?: boolean;
        rewardsMember?: boolean;
    }
    
    abstract class CustomerType {
        protected abstract customerInfo?: IUserInfo;
    }
    
    class Default extends CustomerType {
        protected customerInfo: IUserInfo = {};
        constructor(){
            super();
        }
        
        get _customerInfo(){
            return this.customerInfo ?? {}
        }
        
        set _customerInfo(customerInput: IUserInfo){
            let input: keyof IUserInfo;
            for(input in customerInput){
                if(customerInput[input] === undefined) continue;
                    this.customerInfo = customerInput[input];
            }
        }
    }

Here is the Typescript error:

Type 'boolean | CustomerGroup | undefined' is not assignable to type 'IUserInfo'. Type 'undefined' is not assignable to type 'IUserInfo'.(2322)

Typescript cannot infer that the customerInput[input] is not undefined, despite the check right above the error.

Here is a link to the typescript playground: https://www.typescriptlang.org/play?#code/FAUwdgrgtgBAwhAzgFwPZRAJwOKdRABxgG9gZyYAxAS0xQBVqNMYBeGAIgDNaUZkmWDgBoyFAMoQARogDGmalKxtOiaYgVLMHYAF9gwamGRYuAQ1kgYASQCqiLNbBdUJMQHM8hAPwAueEhozLj4BADcYjx0yHAANmaIiACyIFBafjBSqKixIGZgEeSYIADuZpgAJsmp6f5ZOXkFegbAZjLImBbIMLLxiQEo6Fj0AJ4EVqTkBHgmsiYVMG0onXM9gUOYTi4Zdg6bzqgR sC9CYgwACIg5hCx3SAAHiZgVQNBw2MTwNOos-Nrg2YW1Q-l2jgOKmIukKFFkqDAywgc1QmAAFABKSYUbFqcZo9Ew8jHbHuEDdAD6snWQIOGKx2KKZIgmDA-AAFtREAA6KmA8EuGDebwkfTY4kUBwU3nvfYuVHSjZOAgQZCg z81CYsQMmC5bpGZWqmAAaxAI1QXBs6tlh21DJcaINKpgRgBMqVKq1Op11C48up4MNAG0ncgALpsVjsCAva5GEAVdE9eECSAgQne8jIDnchU0gXsPOBlUhsCGsMZsV2-S6IA

Here is a link to a codepen: https://codepen.io/darylshy/pen/YzjWBoG?editors=0011

CodePudding user response:

The correct code should be:

set _customerInfo(customerInput: IUserInfo){
   let input: keyof IUserInfo;
    for(input in customerInput){
        if(customerInput[input] === undefined) continue;
        this.customerInfo[input] = customerInput[input];
    }
}

Not sure why TS is complaining at first glance to be honest.

It would be better written like so anyway:

set _customerInfo(customerInput: IUserInfo){
    Object.assign(this.customerInfo, customerInput)
}

Your loop didn't make much sense: you were overwriting this.customerInfo, which is a collection of values, with a single value. That was your mistake.

I'd also like to add that if you don't understand what TS is telling you, you can always try to run the code and inspect the return value. You should be writing tests anyway, why not write them as you code? It will get you unstuck much sooner.

For instance if you had written the following test

const a = new Default();

a._customerInfo = { rewardsMember: true };

t.deepEqual(a._customerInfo, { rewardsMember: true })

The test runner would have told you that a._customerInfo is true instead of { rewardsMember: true }

  • Related