假设有以下示例代码:
type Action = "GET" | "POST" | "PUT";
type Handler<A extends Action> = (action: A) => void;
const handlers: Partial<Record<Action, Handler<Action>>> = { };
function register<A extends Action>(action: A, handler: Handler<A>) {
/*
Error:
Type 'Handler<A>' is not assignable to type 'Partial<Record<Action, Handler<Action>>>[A]'.
Type 'Handler<A>' is not assignable to type 'Handler<Action>'.
Type 'Action' is not assignable to type 'A'.
'Action' is assignable to the constraint of type 'A', but 'A' could be instantiated with a different subtype of constraint 'Action'.
Type '"GET"' is not assignable to type 'A'.
'"GET"' is assignable to the constraint of type 'A', but 'A' could be instantiated with a different subtype of constraint 'Action'.
*/
handlers[action] = handler;
}
Run Code Online (Sandbox Code Playgroud)
据我了解,发生上述错误是因为A允许类型大于Action(例如可能是Action | "DELETE"),但我的handlers记录只允许exactly联合Action类型。我可以通过一些方法来解决这个问题:
handler。这会使编译器安静下来,但实际上并没有解决问题,因为用户仍然可以将更大的类型传递给register. 另外,演员阵容从来都不是理想的:)register(action: Action, handler: Handler<Action>). 现在这意味着 和action不必handler就它们的类型达成一致,这可能会导致运行时错误。由于这些解决方法都不能完全解决问题,有没有办法让我强制执行action并且handler都使用相同的A,同时也不允许A大于Action?
编辑:
我实际上发现了一个更小的最小重现,它给出了相同的错误:
function foobar<T extends "foo" | "bar">(func: (arg: T) => void): (arg: "foo" | "bar") => void {
return func;
}
Run Code Online (Sandbox Code Playgroud)
其中 return 语句给出与上面相同的错误。这揭示了实际的问题:T实际上可以小于联合,因此您最终可能会传入一个可以处理比预期更少的情况的函数。
除非实现了这个或这个,否则就不存在一个好的解决方案,所以只需投射它即可。
A extends Action不允许A有任何比 更大的东西Action。它不可能是Action | "DELETE"。A extends B意味着它A是 的子类型B,或者如果您有一个类型的值,A它也必须是 类型的B。type 的值Action | "DELETE"不需要是 type 的值Action,因为它可能是"DELETE"。
您可能希望handlers将其定义为将键与值相关联的映射类型:const handlers: {[K in Action]?: Handler<K>} = {};
但由于上面第一句中提到的原因,编译器看不到这一点handlers[action]并且兼容的问题仍然存在。TypeScript 中对相关handler类型的支持并不多,而且自从 TS3.5对索引访问变得更加严格以来,这种情况只会变得更糟。
| 归档时间: |
|
| 查看次数: |
4723 次 |
| 最近记录: |