A type that is homomorphic mapped (explained in this What does "homomorphic mapped type" mean?) takes the shape of {[K in keyof T]: ⋯T[K]⋯}
, keeping intact the optional and/or readonly characteristics of the mapped properties. Optional property goes in, optional property comes out:
type A = { x?: string | null };
// ^? { x?: string | null | undefined }
type B0 = { [Key in keyof A]: Key };
// ^? { x?: "x" | undefined }
Furthermore, optional properties inherently encompass undefined
within their scope. When you inspect the type using IntelliSense, you will typically see | undefined
included, as visible in the example above. (Assuming --strictNullChecks
is enabled but not the --exactOptionalPropertyTypes
compiler option.)
Consequently, when you access it to extract values as a union, any optional property results in adding an undefined
to the union:
type B = B0[keyof A];
// ^? "x" | undefined
Hence, the presence of | undefined
isn't due to keyof
specifically, but rather from the | undefined
linked to the type being mapped.
As indicated, you have the option to utilize Required
or the -?
mapping modifier to resolve this issue:
type B = { [K in keyof A]-?: K }[keyof A];
// type B = "x"
Check out the code on the TypeScript Playground link