Description
π Search Terms
"mapped types", "key remapping", "indexed access type"
π Version & Regression Information
- This is the behavior in every version I tried (5.2.2, 5.3.2, 5.4.0-dev.20240119), and I reviewed the FAQ.
β― Playground Link
π» Code
export type NodeInterface<LiteralType extends string = string> = {
type: LiteralType;
};
export type SerializedStringMeta = Record<string, string | undefined>;
export type DomExtractedNodeData = {
nodeType: string;
meta: SerializedStringMeta;
};
export type NodeToDomSerializer<Node extends NodeInterface> = (
node: Node,
) => SerializedStringMeta;
export type NodeMetaSerializerMap<NodeUnion extends NodeInterface> = {
[Node in NodeUnion as Node['type']]: NodeToDomSerializer<Node>;
};
const has = <T extends object>(
o: T,
k: string | number | symbol,
): k is keyof T => Object.hasOwn(o, k);
const trySerializeUsingMapping = <T extends NodeInterface>(
mapping: NodeMetaSerializerMap<T>,
node: T,
) => {
if (has(mapping, node.type)) {
const a = mapping[node.type];
const b = a(node);
return a(node);
}
throw new TypeError('Unsupported node type');
};
π Actual behavior
Type of the variable a
is correctly inferred to be NodeMetaSerializerMap<T>[keyof NodeMetaSerializerMap<T>]
. To be rigorous this should be NodeToDomSerializer<T>
.
Type of variable b
, however, is inferred to be any
. Compiler doesn't warn me about this, and as you can understand, this could lead to all sorts of potential any
-leakages. Compiler option strict
doesn't change this as well.
π Expected behavior
I expect type of the variable b
to be correctly inferred as SerializedStringMeta
, or at least some kind of error to be shown in the call site of a(node)
.
Additional information about the issue
I've also reviewed this pull request. I've tried the following work-around:
type TypeToNodeMapping<Node extends NodeInterface> = {
[K in Node as Node['type']]: Node;
};
export type NodeMetaSerializerMap<NodeUnion extends NodeInterface> = {
[K in keyof TypeToNodeMapping<NodeUnion>]: NodeToDomSerializer<
TypeToNodeMapping<NodeUnion>[K]
>;
};
This results in a following error being shown at the call site for a(node)
:

This also seems like a weird behaviour, because I've tested different things and verified that TypeToNodeMapping<NodeInterface>[NodeInterface['type']]
is correctly resolved as NodeInterface
. Seems to me like the compiler is for some reason refusing to unpack the types further, despite the fact it seems safe to use the upper boundary NodeInterface
when resolving types for T
.