1

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
}
rgdzv
  • 433
  • 5
  • 18

2 Answers2

1

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.

Tobias S.
  • 21,159
  • 4
  • 27
  • 45
0

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];
mstephen19
  • 1,733
  • 1
  • 5
  • 20
  • How does that help with the TypeScript problem the user is asking about? The same compile-time error will be issued for this code. – Heretic Monkey May 10 '22 at 19:12
  • @HereticMonkey I answered his first question. Awaiting an answer for the second question, as I face this problem as well – mstephen19 May 10 '22 at 19:14
  • @mstephen19, good for address but if I need to get values from company or other prop? In that case I need to repeat your code – rgdzv May 10 '22 at 19:37