I recently found what looks like a situation where the TS typeof operator loses important type information. I suspect it really comes down to a misunderstanding on my part but fortunately it's easy to illustrate:
const omit = <T extends object, K extends keyof T>(dict: T, key: K) => {
return dict as Omit<T, K>;
};
// implicit object
const obj = { foo: 1, bar: 2 };
// full type info available after "omit"
const obj2 = omit(obj, "bar");
// type info is "lossy"
type O2 = typeof obj2;
In vs-code when I hover over obj2 it's type is expressed as: Omit<{foo: number, bar: number}, "bar">. This is both what I expected and wanted. However, when I hover over the type O2 I get a type of { foo: number }.
You can see how these two types are similar but the first one contains more type information as it has preserved the overall shape of the object along with the omission from that shape.
This is also the case in the slightly more explicit situation where the original object's type is stated:
// explicit object
type Start = { foo: number, bar: number};
const obj3: Start = {foo: 1, bar: 2};
// full type info available on obj4
const obj4 = omit(obj3, "bar");
// again "reduced" to just the remaining props
type O4 = typeof obj4;
Is there any way in which the full type information can be captured by a downstream type definition like O2 or O4? I do understand that in many cases, this reduced state is actually preferred as it both simpler and accurate making it easier to grok but in more complex environments this missing information is useful to preserve.
I have noticed that if I create an identity function and assign another variable through the identity function it preserves type information. This is useful though doesn't entirely address my question.
Example:
const omit = <T extends object, K extends keyof T>(dict: T, key: K) => {
return dict as Omit<T, K>;
};
function identity<T>(thing: T):T {
return thing;
}
// implicit object
const obj = { foo: 1, bar: 2 };
// full type info available after "omit"
const obj2 = omit(obj, "bar");
const myObj = identity(obj2);
type O2 = typeof myObj;
typing for both
obj2andmyObjare the same and complete;O2is still lossy.