0

I have data coming that needs to map to the interface I declared for the response when I assign keys to object it shows error TS7017: Element implicitly has an 'any' type because type 'Idetails' has no index signature. any idea to fix it?

main.ts

public Responsehandler(@Body() data: any): any {
    const response: Idetails = {} as Idetails;
    if (data.details === undefined || data.details === null) {
        return data;
    }
    if (data.details) {
        response.details.lineOfBusiness = "PBM";
        Object.keys(data.details).forEach((key) => {
            response.details[key] = data.details[key]
        });
    }
    return response;
}

interface.ts

export interface Idetails {
 primary:balanceDetails;
 secondary: balanceDetails;
}

export interface balanceDetails {
     beginningBalance: string;
     endingBalance: string;
}
hussain
  • 6,587
  • 18
  • 79
  • 152
  • What means "map to interface"? I assume you want ``response`` variable to have ``primary`` and ``secondary`` property? – Buczkowski Oct 23 '18 at 17:43
  • because primary and secondary both has similar keys so i want response to map keys from data – hussain Oct 23 '18 at 17:52
  • Possible duplicate of [How to assign values to object that has similar keys?](https://stackoverflow.com/questions/52933808/how-to-assign-values-to-object-that-has-similar-keys) – ConnorsFan Oct 23 '18 at 18:01

1 Answers1

1

I'm guessing that you're running into the issue where Object.keys(obj) returns string[] instead of something like (keyof typeof obj)[]. It's a common issue that gets reported a lot. The reason Object.keys() has to return string[] is because types in TypeScript are open in the sense that an object has to have at least the properties described by a type for it to match. So the only type-safe return value is string[]. See this comment for more information.

This means that assuming data.details is of type Idetails (I don't see that in your code... data is just of type any; you should tighten that up), all you know is that it has at least the primary and secondary properties, but it might have more. For example, data.details might be

const details = {
  primary: { beginningBalance: "$0", endingBalance: "$200" },
  secondary: { beginningBalance: "25¢", endingBalance: "10¢" },
  tertiary: { beginningBalance: "₿100,000", endingBalance: "₿0.001" }
}

And therefore key is not a valid index into response.details, since key might be "teritary".


The easiest way to deal with this is just to assert that Object.keys(data.details) returns just the keys you know about. Sure, it's possible that at runtime there will be extra keys, and the code will just copy those extra properties into response.details... which is probably harmless since it doesn't stop response.details from being a valid Idetails. Here's how you could do it:

(Object.keys(data.details) as (keyof Idetails)[]).forEach((key) => {
  response.details[key] = data.details[key]; // okay
});

Notice that we are using the as keyword to assert that Object.keys(data.details) returns a (keyof Idetails)[]. Now, key is inferred to be "primary" | "secondary", and the compiler is happy with the assignment.


There are other ways to deal with the issue in the case where you want to prevent copying extra properties, such as manually specifying the array of keys to copy without inspecting data.details at all:

// helper function to narrow array to string literals
const stringLiterals = <T extends string[]>(...args: T) => args;

// stringLiterals("primary", "secondary") is inferred as type ["primary", "secondary"]
stringLiterals("primary", "secondary").forEach((key) => {
  response.details[key] = data.details[key]; // okay
});

This is now completely type safe and doesn't require any type assertions, but it might be more trouble than it's worth.


Hope that helps; good luck!

jcalz
  • 264,269
  • 27
  • 359
  • 360
  • i tried your Object.keys solutions getting this error `Array type using 'T[]' is forbidden for non-simple types. Use 'Array' instead. (array-type)` – hussain Oct 23 '18 at 18:30
  • `(Object.keys(data.details) as Array)` resolved tslint issue – hussain Oct 23 '18 at 18:33
  • Those are equivalent; tslint can be configured to report or not report that warning but it’s a matter of style, not correctness. – jcalz Oct 23 '18 at 18:40
  • not sure it still says can not set property of "primary" undefined – hussain Oct 23 '18 at 18:56
  • I can't reproduce that; is that a runtime error? It is likely to be a separate issue from what you've posted about; please review what constitutes a [mcve]. – jcalz Oct 23 '18 at 19:03
  • thanks for the help yes it is run time error let be dig into this – hussain Oct 23 '18 at 19:12