Description
Using a Homomorphic Mapped Type For Omit
interface Obj {
readonly a: string;
b?: number;
c: boolean;
}
// "homomorphic" - preserves all modifiers in BoxedObj
type Boxify<T> = {
[K in keyof T]: { value: T[K] }
}
type BoxedObj = Boxify<Obj>;
// non-"homomorphic" - loses information for BoxedObj2
type Boxify2<Keys extends keyof Obj> = {
[K in Keys]: { value: Obj[K] }
}
type BoxedObj2 = Boxify2<keyof Obj>;
-
Today,
Omit
is non-homomorphic. Will not preserve shape.- Why?
keyof
on its own does not retain its source type.
-
Today, we can write
Omit
with anas
clause withExclude
- still considered "homomorphic" (even though at this point "homomorphic" is a misnomer).type Omit<T, DroppedKeys extends PropertyKey> = { [K in keyof T as Exclude<K, DroppedKeys>]: T[K] };
-
Switching
Omit
at this point would likely be breaky.- Existing code where
interface
s extends fromOmit
- Existing code where
-
Using a "homomorphic"
Omit
makes code flow work better because it can produce union types. -
Could choose to have
Omit
(original) andMappedOmit
- orOmit
(improved) andLegacyOmit
.- Swapping
Omit
to the "better" version is breaky - at least one package breaks.
- Swapping
-
We really don't want to have 2
Omit
s, and want to push on an improvedOmit
.- Existing code can write
Pick<T, Omit<keyof T, DroppedKeys>>
- Existing code can write
-
Wait, should
Pick
be fixed to use a homomorphic mapped type too?- OH NOOOOOOOOOOOOOOOOOOOOOOOOOOOO
- Almost had a slam dunk on just fixing
Omit
.
-
If you fix
Pick
, thenOmit
defined asPick<T, Exclude<keyof T, DroppedKeys>>
is also homomorphic, right?- Almost - but
Omit
is not going to distribute, when we writeExclude<keyof T, DroppedKeys>
- Almost - but
-
So each of them need to be written as their own mapped types written in terms of
keyof T
. -
Note: the
as
clause forPick
should use an intersection, notExtract
- Why?
- Intersections are much faster for unions of literals.
- What's the difference in the general case?
- For literal types, they're the same.
- For object types and generic types, intersections combine and don't always "eradicate" other types to
never
.
- Why?