4
votes

Here is a class:

export class Survey {
    isCompleted: boolean;
    response: {
        'devices': string[];
        'languages': string[];
        'frameworks': string[];
        'backend': string[];
    };
}

I'm getting the "Element implicitly has an 'any' type because type '...' has no index signature" error when trying the following:

return Object.keys(user1.response).map(key => {
        return percentageMatch(user1.response[key], user2.response[key]) * categoryScores[key];
    })

user1 and user2 are instances of the Survey class.

I know how to set an index signature with simple object literals but how do I do it with the properties of the response object, which is itself a property of the Survey class.

2

2 Answers

8
votes

That required adding an index type [key: string]: string[] to the response type:

export class Survey {
    isCompleted: boolean;
    response: {
        [key: string]: string[],
        devices: string[],
        languages: string[],
        frameworks: string[],
        backend: string[],
    };
}

You can check it out in the TypeScript Playground demo that I created.

You could also consider reducing repetition here and extract known keys to a string literal type:

type ResponseKeys = 'devices' | 'languages' | 'frameworks' | 'backend';

export class Survey {
    isCompleted: boolean;
    response: {
        [key in ResponseKeys]: string[]
    };
}
1
votes

Index signature is required because Object.keys() loses type information and returns string arrays.

You can introduce your own function which is similar to Object.keys() but is declared to return an array of actual object keys:

function typedKeys<T>(o: T): (keyof T)[] {
    // type cast should be safe because that's what really Object.keys() does
    return Object.keys(o) as (keyof T)[];
}

Then, there will be no error in a function that iterates keys of one object and accesses another object, if the objects are of the same type:

export class Survey {
    isCompleted: boolean;
    response: {
        'devices': string[];
        'languages': string[];
        'frameworks': string[];
        'backend': string[];
    };
}

function f(user1: Survey, user2: Survey) {
    return typedKeys(user1.response).map(key => {
        return user1.response[key] == user2.response[key];
    })
}