I can't seem to figure out the issue at hand. There is a straightforward tagged union in my code:
type MyUnion =
| { tag: "Foo"; field: string; }
| { tag: "Bar"; }
| null;
Now, there's this generic function that I'm dealing with:
const foo = <T extends MyUnion>(value: T) => {
// Narrowing works as expected.
if (value === null) { return; }
const notNull: NonNullable<T> = value;
// Narrowing works with field access, but not with `Extract` type.
if (value.tag !== "Foo") { return; }
const s: string = value.field; // Field can be accessed -> all good here.
const extracted: Extract<T, { tag: "Foo" }> = value; // error!
};
The error pops up on the last line:
Type 'T & {}' is not assignable to type 'Extract<T, { tag: "Foo"; }>'.
This has been quite baffling. What could possibly be causing this discrepancy? When I write it outside the function without using generics like
Extract<MyUnion, { tag: "Foo" }>
, everything falls into place perfectly. So, what's the underlying issue here?
Note: In my actual code, the function has another parameter callback: (v: Extract<T, { tag: "Foo" }>) => void. Invoking that callback triggers the same typing error. Therefore, my aim is to somehow make this function work smoothly: Playground. Essentially, the function foo
performs specific checks on a value, handles special cases, and then calls the callback with the sanitized value. Surely TypeScript should support such abstractions?