I encountered a TypeScript error when using reduce to create an object where the keys and values are the same. Here's my code:
const weather = {
sunny: '맑음',
partlyCloudy: '구름 조금',
mostlyCloudy: '구름 많음',
cloudy: '흐림',
rain: '비',
snow: '눈',
rainOrSnow: '비 또는 눈',
snowOrRain: '눈 또는 비',
thunderstorm: '천둥번개',
fog: '안개',
yellowDust: '황사'
};
type WeatherKeys = keyof typeof weather;
type WeatherKeyMap = { [K in WeatherKeys]: K };
const foodKeys = (Object.keys(weather) as WeatherKeys[]).reduce((acc, key) => {
acc[key] = key;
return acc;
}, {} as WeatherKeyMap);
I'm receiving the following error on acc[key] = key:
Type 'string' is not assignable to type 'never'.
How can I resolve this error?
You've run into TypeScript's inability to deal with so-called correlated union types as described in microsoft/TypeScript#30581. The line acc[key] = key
has key
appear twice, where key
is of the WeatherKeys
union type. TypeScript doesn't check each possible value of key
to see if it's valid. Instead, it just treats each utterance of key
as essentially independent values of the same union type. It's almost like it's analyzing acc[key1] = key2
where key1
and key2
are both of type WeatherKeys
, and complaining that it's not safe.
The recommended approach in cases like this is to try to refactor to generics in a particular form such that the compiler can be sure that both sides of the assignment are the same. The general approach is detailed in microsoft/TypeScript#47109.
For this particular example, I'd write it like this:
const foodKeys = (Object.keys(weather) as WeatherKeys[]).reduce(
<K extends WeatherKeys>(acc: { [P in K]: P }, key: K) => {
acc[key] = key;
return acc;
}, {} as WeatherKeyMap);
Now the callback is generic, where key
is of the generic type K
constrained to WeatherKeys
, and where acc
is of the type {[P in K]: P}
which is a supertype of WeatherKeyMap
. TypeScript is happy to accept the callback because it matches the expected input type. And inside the callback, TypeScript is happy to accept the assignment acc[key] = key
because both sides of the assignment are equivalent to K
.