Home > Software engineering >  How to set a good condition for dynamic property of object
How to set a good condition for dynamic property of object

Time:05-11

I have an array of objects:

export const inputsArray: InputAttributes[] = [
    {
        label: 'Name',
        type: 'text',
        name: 'name',
        required: true
    },
    {
        label: 'User name',
        type: 'text',
        name: 'username',
        required: true
    },
    ...
]

and map it like so:

const inputs = inputsArray.map(input => {
    const value = (input.name === 'street' || input.name === 'city' || input.name === 'zipcode')  // <=  PROBLEM : NOT EFFECTIVE CONDITION!
        ? user?.address?.[input.name] 
        : user?.[input.name]   // <= TYPESCRIPT PROBLEM 

    return (
        <Input
            key={input.name}
            defaultValue={value} 
            input={input}
            disabled={disabled}
            onChange={hadndleChange}
        />
    )
})

Info in user:

export interface User {
    id: number,
    name: string,
    username: string,
    email: string,
    address: {
        street: string,
        city: string,
        zipcode: string,
    }
    phone: string,
    website: string,
    company: {
        name: string
    }
}
  1. As you can see, inside map method I have a condition for value. But it's not good, because if user object have more additional properties inside address or company or other property with object as value, I need to put more conditions inside. Is there any better pattern for doing that?
  2. user?.[input.name] Typescrips says that "type 'string' can't be used to index type 'Users'". I can avoid that problem by setting [key: string] : any inside User interface, but is there any better pattern?

Input attributes interface if needed:

export interface InputAttributes {
    label: string,
    type: string,
    name: string,
    pattern?: string,
    required: boolean
}

CodePudding user response:

You should modify the InputAttributes interface like this:

type DeepKeyOf<T> = {
  [K in keyof T]: T[K] extends Record<string, any> ? DeepKeyOf<T[K]> : K
}[keyof T]

export interface InputAttributes {
    label: string,
    type: string,
    name: DeepKeyOf<User>,
    pattern?: string,
    required: boolean
}

Now TypeScript knows that the name property must contain some key of User.

CodePudding user response:

Store the values in an array, then use Array.prototype.some(). This will let you support as many names as you want.

const names = ['street', 'zipcode', 'city'];
const value = names.some((name) => input.name === name) ? user?.address?.[input.name] : user?.[input.name];
  • Related