【问题标题】:Extending a union type exactly完全扩展联合类型
【发布时间】:2019-11-02 15:30:06
【问题描述】:

假设如下示例代码:

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;
}

据我了解,发生上述错误是因为A 允许类型大于 大于Action(例如它可能是Action | "DELETE"),但我的handlers 记录只允许@ 987654331@ Action 联合类型。有一些方法可以解决这个问题:

  • 在内部放弃handler。这会使编译器安静下来,但实际上并没有解决问题,因为用户仍然可以将更大的类型传递给register。另外,演员阵容从来都不是理想的:)
  • 使函数具体化,register(action: Action, handler: Handler&lt;Action&gt;)。这意味着 actionhandler 不必在类型上一致,这可能会导致运行时错误。

由于这些解决方法都不能完全解决问题,我有没有办法强制 actionhandler 都使用相同的 A,同时也不允许 A 大于Action?


编辑:

我实际上发现了一个更小的最小重现,它给出了相同的错误:

function foobar<T extends "foo" | "bar">(func: (arg: T) => void): (arg: "foo" | "bar") => void {
    return func;
}

return 语句给出与上述相同的错误。这揭示了实际问题:T 实际上可能比联合,因此您最终可能会传入一个可以处理比预期更少的情况的函数。

【问题讨论】:

  • 除非实现了thisthis,否则不会存在一个好的解决方案,所以只需转换它
  • A extends Action 不允许AAction“大”。它不能是Action | "DELETE". A extends B` 意味着AB子类型,或者如果你有一个A 类型的值,它也必须是@987654352 类型@。 Action | "DELETE" 类型的值不需要是 Action 类型的值,因为它可能是 "DELETE"
  • 您可能希望将handlers 定义为将键与值相关联的映射类型:const handlers: {[K in Action]?: Handler&lt;K&gt;} = {};
  • 但是由于@Austaras 提到的原因,编译器无法看到handlers[action]handler 兼容的问题仍然存在。 TypeScript 中对 correlated 类型的支持不是很好,而且这种情况只会变得更糟,因为 TS3.5 通过索引访问获得了更多的 strict
  • 看看this answer,它试图完全解释这种信息的含义。让我知道它是否能解决您的问题。

标签: typescript


【解决方案1】:

除非实现thisthis,否则不会存在好的解决方案,因此只需强制转换即可。

A extends Action 不允许AAction“大”。不能是Action | "DELETE"A extends B 表示AB 的子类型,或者如果您有A 类型的值,它也必须是B 类型。 Action | "DELETE" 类型的值不需要是 Action 类型的值,因为它可能是 "DELETE"

您可能希望将 handlers 定义为将键与值相关联的映射类型:const handlers: {[K in Action]?: Handler&lt;K&gt;} = {};

但是由于上面第一句中提到的原因,编译器无法看到handlers[action]handler 兼容的问题仍然存在。 TypeScript 中对 correlated 类型的支持不是很好,而且这种情况只会变得更糟,因为 TS3.5 通过索引访问获得了更多的 strict

【讨论】:

  • 这是对已投票的 cmets 的复制和粘贴,已在问题下发布 - 我将其重新发布为答案,以防这些 cmets 被删除。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多