I'm curious why the compiler requires explicit type declaration for values in the `Policies` type, even though they can only be one of my specified `Types`.
type Types = 'baz' | 'bar';
// tagged union types
type Foo<T extends Types> = { type: T; }
type FooOne = { one: string } & Foo<'baz'>;
type FooAnother = { another: string } & Foo<'bar'>;
type Predicate<T extends Types> = (e: Foo<T>) => boolean;
type Policies = {
[P in Types]: Predicate<P>
}
const policies: Policies = {
baz: (e: FooOne) => e.one === 'neo',
bar: (e: FooAnother) => e.another === 'morphieus'
}
// this method receives a union type
function verify(e: FooOne | FooAnother) {
// these work as expected
if (e.type === 'baz') {
const policy1 = policies[e.type]; // ide says this type is a Predicate<'baz'>
const result1 = policy1(e); // fine
} else {
const policy2 = policies[e.type]; // and this is a Predicate<'bar'>
const result2 = policy2(e); // fine
}
// however, this doesn't work even though e.type is known to be 'baz' | 'bar', and the keys in policies can only be 'baz' | 'bar'
const policy3 = policies[e.type]; // ide says this type is a Predicate<'bar'> | Predicate<'baz'>
const result3 = policy3(e); // error Cannot invoke an expression whose type lacks a call signature
// this works if I provide the compiler with information about what is at policies[T]
const policy4: Predicate<Types> = policies[e.type]; // Predicate<'baz' | bar'>
const result4 = policy4(e); // fine
}