Having a structure like this:
enum Company {
Google,
Facebook
}
function getRating(company: Company) {
const ratingMap = {
[Company.Google]: 1000,
[Company.Facebook]: 2000
}
const result = ratingMap[company]
return result
}
TypeScript understands that result
is a number
. But if I list not all the Company
values in the ratingMap
, then the result
has type any
. I want ts to resolve this expression into number | undefined
rather than any
.
function getRating(company: Company) {
const ratingMap = {
[Company.Google]: 1000,
// [Company.Facebook]: 2000
}
const result = ratingMap[company] // Element implicitly has an 'any'
return result
}
I know I can set a type for ratingMap
like this:
const ratingMap: Partial<Record<Company, number>>
and it would give me expected number | undefined
. But I think there should be a simpler elegant solution. Maybe a specific tsconfig property.
Any ideas how to achieve this?
CodePudding user response:
Have you tried
function getRating(company: Company): number | undefined {
const ratingMap = {
[Company.Google]: 1000,
[Company.Facebook]: 2000
}
const result = ratingMap[company]
return result
}
This should do exectly what you want.
CodePudding user response:
Use the es6 map. Playground here.
enum Company {
Google,
Facebook
}
function getRating(company: Company) {
const ratingMap = new Map<number, number>([
[Company.Google, 1000],
[Company.Facebook, 2000]
]);
const result = ratingMap.get(company);
return result
}
let ratingValue : number | undefined = getRating(Company.Google);
alert(ratingValue);
CodePudding user response:
The type of the result
is not any
, it is just inferred as any
due to the error. Specifically, the error is that you index a type that lacks an index signature with a numeric union of 0 | 1
(values of Company.Google
and Company.Facebook
respectively) while deferring inference of what type ratingMap
is to the compiler. Unsurprisingly, the type is inferred as:
const ratingMap: {
0: number;
}
If you enable the suppressImplicitAnyIndexErrors
backwards compatibility compiler flag, you will notice that the error disappears when indexing the ratingMap
with Company
(although the result
will still be inferred as any
because the flag simply suppresses the error).
You need to explicitly tell the compiler what ratingMap
is: a partial record with keys of type Company
and values of type number
, which is trivially expressed as Partial<Record<Company, number>>
. The result
type will correctly be inferred as number | undefind
then:
enum Company {
Google,
Facebook
}
function getRating(company: Company) {
const ratingMap: Partial<Record<Company, number>> = {
[Company.Google]: 1000
};
const result = ratingMap[company]; // number | undefined
return result;
}
An alternative solution is to ensure the compiler that company
is definitely a key on the ratingMap
with a user-defined type guard. This is probably the most type-safe way:
enum Company {
Google,
Facebook
}
const isKeyof = <T>(obj:T, key: string | number | symbol): key is keyof T => key in obj;
function getRating(company: Company) {
const ratingMap = {
[Company.Google]: 1000
};
const result = isKeyof(ratingMap, company) ? ratingMap[company] : void 0; // number | undefined
return result;
}