Joseph
Joseph

Reputation: 4685

How can I test if Object has value by key without ignoring TypeScript warning: 'string' can't be used to index type?

I want to write a simple function that looks like this

const options = {
  option1: "option1",
  option2: "option2",
  option3: "option3",
} as const;

function getOption(str: string) {
  if (options[str]) {
    /**
     * Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ readonly option1: "option1"; readonly option2: "option2"; readonly option3: "option3"; }'.
     * No index signature with a parameter of type 'string' was found on type '{ readonly option1: "option1"; readonly option2: "option2"; readonly option3: "option3"; }'.ts(7053)
     */
    return options[str];
  }

  return "defaultOption";
}

Now, I know I can solve it like this

const options = {
  option1: "option1",
  option2: "option2",
  option3: "option3",
} as const;

function getOption(str: keyof typeof options) {
  if (options[str]) {
    // Solved! No warning!
    return options[str];
  }

  return "defaultOption";
}

But it seems like I'm lying to my code...

For example, if I write something like this

function isArray(arg: any[]) {
  return Array.isArray(arg);
}

It will be weird to use this util because these two reasons

  1. If I know it's array before runtime, I don't need to use this util at all
  2. If I don't know if it's an array before runtime then TypeScript will yell again!

And here is another example:

Suppose I want to get the 'defaultOption' by passing argument null, it will be so weird

// TypeScript yells!
// Argument of type 'null' is not assignable to parameter of type '"option1" | "option2" | "option3"'.ts(2345)
console.log(getOption(null));
// => 'defaultOption'

I will have to do this to suppress the warning

// No more warning.
// Making null as any is weird
getOption(null as any);
// => 'defaultOption'

As you can see, I have to explicitly convert the null as any in order to get my 'defaultOption'

Is there any way I can improve my getOption util?

Upvotes: 3

Views: 243

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 370639

Ideally, you'd want to type getOption's argument to be a key of the options, to require callers to pass a sensible argument. But if you want to permit any argument may be passed to getOption, including a key, or a generic string, or any value at all like null, then type the argument as unknown, then have TS narrow down whether the argument is one of options keys:

function getOption(str: unknown) {
    if (str === 'option1' || str === 'option2' || str === 'option3') {
        return options[str];
    }
    return "defaultOption";
}

Upvotes: 1

Related Questions