14

In TypeScript, I declare an interface like this:

export default interface MyDTO {
    readonly num: string;
    readonly entitle: string;
    readonly trb: string;
    readonly ucr: string;
    readonly dcr: string;
    readonly udm?: string;
    readonly ddm?: string;
}

With a function, I would like to access the value of a property, whose name is contained in a variable.

private doSomething(dto: MyDTO, property: string): any {
    let label: any;

    if (['dcr', 'ddm'].includes(property)) {
        label = doSomethingElse(dto[property]);
    } else {
        label = dto[property];
    }
    
    return label;
}

Unfortunately, TypeScript gives me the following error message :

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'MyDTO'. No index signature with a parameter of type 'string' was found on type 'MyDTO'.ts(7053)

Anyone have an idea, please ?

Thank you

Answerrer
  • 2,803
  • 4
  • 8
  • 18
  • Does this answer your question? [How do I prevent the error "Index signature of object type implicitly has an 'any' type" when compiling typescript with noImplicitAny flag enabled?](https://stackoverflow.com/questions/32968332/how-do-i-prevent-the-error-index-signature-of-object-type-implicitly-has-an-an) – mhodges Jul 06 '20 at 15:45

3 Answers3

20

The reason for this is because MyDTO has explicitly named properties, but you're using a generic string as an index, so TypeScript is saying that it can't guarantee that whatever string is passed into your doSomething function will actually match a property name on your interface.

An excellent workaround for this that was introduced in TypeScript 2.1 is keyof. This allows you to explicitly type something as a key of a certain class/interface.

This will A. get rid of the TS error you're seeing, and B. also check to make sure that any callers of your function actually pass a valid key.

export default interface MyDTO {
    readonly num: string;
    readonly entitle: string;
    readonly trb: string;
    readonly ucr: string;
    readonly dcr: string;
    readonly udm?: string;
    readonly ddm?: string;
}

function doSomething(dto: MyDTO, property: keyof MyDTO): any {
    let label: any;

    if (['dcr', 'ddm'].includes(property)) {
        label = doSomethingElse(dto[property]);
    } else {
        label = dto[property];
    }
    
    return label;
}

doSomething(obj, "foo") // is a TS error
doSomething(obj, "num") // is valid
mhodges
  • 10,938
  • 2
  • 28
  • 46
  • Indeed this corrects me the case. But I have a somewhat special case, the value I have in property is not a key to my DTO. This can be a simple string that allows me to make an operation from the property of my DTO but I do not directly recover the property of my object. For example : if (property === 'test') { label = property.num + '-' + property.entitle; }. How to combine the keyof and the type string and that in the code of my function that does not concern me about the type? – Answerrer Jul 07 '20 at 06:33
  • I can do before a test on the type entered : if(typeof property === 'string'). – Answerrer Jul 07 '20 at 06:43
  • How can I test if the value of property is a key to my object ? If I test the type of my object (type string), I always pass even if it is a key to my object. – Answerrer Jul 07 '20 at 07:22
  • @Answerrer I'm not sure I understand the question. Can you update your post with a full example of what is not working for you with this solution? It seems like you're wanting to know if the value of a property on your object is also a key to your object? – mhodges Jul 07 '20 at 16:22
  • 1
    Saved my bacon. Thanks for this! – Joel Hager Nov 01 '21 at 07:47
0

@mhodges, with your suggestions, here is my modified function which seems to work well. However in the following case, I had to add the "as string" otherwise I have the following error:

Type 'string | keyof V 'cannot be used to index type' V'.ts (2536)

public getDefaultComparator(property: keyof V | string, v1: V, v2: V): number {
    let compareReturn = 0;
    if (v1.hasOwnProperty(property)) {
      const compareValue1 = v1[property as string];
      const compareValue2 = v2[property as string];
      if (compareValue1 > compareValue2) {
        compareReturn = 1;
      } else if (compareValue1 < compareValue2) {
        compareReturn = -1;
      }
    }

    return compareReturn;
  }
Answerrer
  • 2,803
  • 4
  • 8
  • 18
-15

In the tsconfig.json file. Set "strict": true --> false.

This worked for me.

  • True, and I got the same error after creating a tsconfig.json file.. The default behavior is strict: true. But I guess setting it to false will just not shine light on the problem. Like out of view, out of problem. If you just want a quick and dirty fix for a small hobby or test, you could do this. But infact you have some problem on your logic. – ChangNoi Dec 29 '22 at 21:21
  • 3
    Setting `strict` to `false` is not something that should be encouraged. – Danchez Mar 03 '23 at 21:06