I am switching a JS project to TS and am struggling with a certain part of my code, which TS complains about and I don't know how to resolve the issue.
In TS, I want to collect data in a dynamic object, called licensesStats
, that in the end might look like this:
{
'DYNAMIC_NAME': {
'Usage count': 2,
'Used by': {
'SOME_DYNAMIC_NAME': null,
'SOME_OTHER_NAME': null,
[...]
}
},
[...]
}
For my licensesStats
object, I have created the following interface
:
interface LicenseStatsCounter {
[key: string]: number;
}
interface LicenseStatsModule {
[key: string]: null;
}
interface LicenseStatsModules {
[key: string]: LicenseStatsModule;
}
interface LicensesStats {
[key: string]:
| LicenseStatsCounter
| LicenseStatsModules;
}
licensesStats: LicensesStats = {}
...
The keys "Usage count" and "Used by" are stored in variables, and so in the code during data collection, there is a command that says:
licensesStats[license][titleUsedBy][moduleNameAndVersion] = null;
But TS complains:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'number | LicenseStatsModule'.
No index signature with a parameter of type 'string' was found on type 'number | LicenseStatsModule'.
What do I have to do in order to satisfying TypeScript's needs?
P.S.: Here is an example TS file for my issue:
const titleUsageCount = 'Usage count';
const titleUsedBy = 'Used by';
interface LicenseStatsCounter {
[key: string]: number;
}
interface LicenseStatsModule {
[key: string]: null;
}
interface LicenseStatsModules {
[key: string]: LicenseStatsModule;
}
interface LicensesStats {
[key: string]: LicenseStatsCounter | LicenseStatsModules;
}
// Exported functions:
export const readLicenses = (argv: Argv) => {
const licensesStats: LicensesStats = {};
const license = 'MIT';
const moduleNameAndVersion = '[email protected]';
licensesStats[license] = licensesStats[license] || {
[titleUsageCount]: 0,
...((argv.summaryPlus as boolean) && { [titleUsedBy]: {} }),
};
(licensesStats[license][titleUsageCount] as number) ;
// This is the problematic line, where TS says:
// Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'number | LicenseStatsModule'.
// No index signature with a parameter of type 'string' was found on type 'number | LicenseStatsModule'.
licensesStats[license][titleUsedBy][moduleNameAndVersion] = null;
}
};
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
With your current setup, typescript reaches licensesStats[license][titleUsedBy]
and sees two possible values: either a number
or an object
(the LicenseStatsModule) and throws an error because a number can't contain a property.
You're going to need some kind of conditional checks to make typescript happy, something like:
const x = licensesStats[license][titleUsedBy];
if(typeof x !== "number"){
const y = x[moduleNameAndVersion];
// ... do something
return;
}
// ... do something
return;