Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In TypeScript, how to set the index signature of an object that is the property of a class?

Tags:

typescript

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.

like image 470
snowfrogdev Avatar asked Jan 04 '23 09:01

snowfrogdev


2 Answers

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[]
    };
}
like image 143
Michał Miszczyszyn Avatar answered May 20 '23 23:05

Michał Miszczyszyn


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];
    })
}
like image 25
artem Avatar answered May 20 '23 23:05

artem