I am trying to use an array of elements as union type, something that became easy with const assertions in TS 3.4, so I can do this:
const CAPITAL_LETTERS = ['A', 'B', 'C', ..., 'Z'] as const;
type CapitalLetter = typeof CAPITAL_LETTERS[string];
Now I want to test whether a string is a capital letter, but the following fails with "not assignable to parameter of type":
let str: string;
...
CAPITAL_LETTERS.includes(str);
Is there any better way to fix this rather than casting CAPITAL_LETTERS
to unknown
and then to Array<string>
?
The standard library signature for
Array<T>.includes(u)
assumes that the value to be checked is of the same or narrower type than the array's elementsT
. But in your case you are doing the opposite, checking against a value which is of a wider type. In fact, the only time you would say thatArray<T>.includes<U>(x: U)
is a mistake and must be prohibited is if there is no overlap betweenT
andU
(i.e., whenT & U
isnever
).Now, if you're not going to be doing this sort of "opposite" use of
includes()
very often, and you want zero runtime efects, you should just widenCAPITAL_LETTERS
toReadonlyArray<string>
via type assertion:If, on the other hand, you feel seriously enough that this use of
includes()
should be accepted with no type assertions, and you want it to happen in all of your code, you could merge in a custom declaration:That will make it so that an array (well, a readonly array, but that's what you have in this example) will allow any parameter for
.includes()
as long as there is some overlap between the array element type and the parameter type. Sincestring & CapitalLetter
is notnever
, it will allow the call. It will still forbidCAPITAL_LETTERS.includes(123)
, though.Okay, hope that helps; good luck!