This code appears to compile in TS 4.3, but not before that (i.e. 4.0.5, etc). It's confusing to me why adding a union to undefined
causes the 2nd assignment to not compile. I'm trying to understand what changed that allows this to work in 4.3, but I'm unsure of what to search for (like a bug fix, etc). Also, I'm looking for a proper workaround before TS 4.3 to be used until the version can be upgraded. For instance, should I just add | undefined
on the the type definition of y
, or is there another way to get the types to fit for the 2nd assignment?
export type StringKeyOf<T> = Extract<keyof T, string>;
export interface GenericEvent<TEventName, TData> {
eventName: TEventName;
data: TData;
}
export type EventTypeByEventName = {
[eventName: string]: any;
};
export type GenericEventsFromMap<TEventMap extends EventTypeByEventName, K2 extends keyof TEventMap> = {
[K in K2]: GenericEvent<K, TEventMap[K]>;
}[K2];
export class EventService<TEventMap extends EventTypeByEventName> {
public setup<T extends Array<StringKeyOf<TEventMap>>>(managerId: string, eventNames: [...T]) {
const y: GenericEvent<T[number], TEventMap[T[number]]> = null as any;
// this assignment works
const z: GenericEventsFromMap<TEventMap, T[number]> = y;
// this assignment gives:
// Type 'GenericEvent<T[number], TEventMap[T[number]]>' is not assignable to type 'GenericEventsFromMap<TEventMap, T[number]>'.
const z2: GenericEventsFromMap<TEventMap, T[number]> | undefined = y;
}
}
The bug in question was microsoft/TypeScript#43152, and was fixed in microsoft/TypeScript#43202, which was merged into the main branch on 2021-03-19 and eventually released with TypeScript 4.3.
How did I find this? If you go to https://typescript.azureedge.net/indexes/pre-releases.json you will see a list of TypeScript pre-release versions that you can use in The TypeScript Playground with the ts=
query parameter. Via binary search, you can fairly quickly locate the last version where the bug is present and the first version where the bug is absent.
In your case, the bug was present in 4.3.0-dev.20210316
but absent in 4.3.0-dev.20210323
. That means we'd need to search for pull requests merged between 2021-03-16 and 2021-03-23 and find which one is appropriate. If we add the "union" and "undefined" terms to our search it narrows things down even further.
As for a workaround for those unlucky enough to be stuck on a version with this bug, it's not clear what a good one is. Many obvious fixes fail run into the same bug, since there's no good reason why X
should not be assignable to X | undefined
. The following type assertion seems to work:
const z: GenericEventsFromMap<TEventMap, T[number]> | undefined =
y as typeof y | undefined; // okay
But whether or not it's advisable is hard to say.