Home > Enterprise >  Accessing object by enum returns any instead of undefined
Accessing object by enum returns any instead of undefined

Time:04-25

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
}

Playground

I know I can set a type for ratingMap like this:

const ratingMap: Partial<Record<Company, number>>

Playground

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;
}

Playground


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;
}

Playground

  • Related