我试图理解为什么需要明确告知编译器Policies
类型的值是什么,即使它只能是我的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 both work, understandably
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
}
// but this doesn't work even though e.type is known to be 'baz' | 'bar', and the keys at 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 hint to the compiler what is at policies[T]
const policy4: Predicate<Types> = policies[e.type]; // Predicate<'baz' | bar'>
const result4 = policy4(e); // fine
}
答案 0 :(得分:3)
这里没有好的解决方案,您将需要某种类型断言来使事情正常运行。
问题在于流程分析无法跟踪值的来源。所以当你写:
const policy3 = policies[e.type];
const result3 = policy3(e);
policy3
将是Predicate<"baz"> | Predicate<"bar">
,但是编译器不知道是哪个。当您调用policy3
时,因为它不知道变量中实际上是哪种函数类型,它将在较新的版本中强制您指定可以与联合中的任何一个函数一起使用的参数。不记得实际上与policy3
和e.type
绑定的policy3(e)
是类型安全的。 (仅供参考:在旧版本的policy3
中,由于它是一个联合体,因此根本无法调用)
此代码有效:
const policy4= policies[e.type] as Predicate<Types> ; // Predicate<'baz' | bar'>
const result4 = policy4(e); // fine
但是Predicate<"baz"> | Predicate<"bar">
与Predicate<Types>
不同。前者是采用baz
的函数或采用bar
的函数,后者是采用baz
或bar
的函数。因此,该调用将起作用,因为您已断言该函数可以接受任何一种参数类型(严格地说,这不是正确的,但就像我说的那样,没有好的解决方案)。