【问题标题】:Conditional type is not being narrowed by if/else branchif/else 分支没有缩小条件类型
【发布时间】:2019-07-30 12:00:49
【问题描述】:

我正在尝试编写一个带有回调的函数,其参数类型取决于非回调函数的参数参数。

我尝试使用typeof 推断类型,该类型可以正确缩小分支中的新声明,但不会缩小回调范围。

type Argument = "Foo" | "Bar";
type CallbackArgument<T extends Argument> = T extends "Foo" ? "One" : T extends "Bar" ? "Two" : never;

function foo(arg: Argument, callback: (callbackArg: CallbackArgument<typeof arg>) => void) {
    if (arg === "Foo") {
        callback("Two") // Works, callbackArg hasn't narrowed.
        const two: CallbackArgument<typeof arg> = "Two" // Error, narrowing seems to work.
    }
}

由于typeof arg 已明显缩小到"Foo",我希望callbackArg 在分支内正确缩小到"One",就像它对新声明(如two)所做的那样。

我正在使用 Typescript 3.5.2。

【问题讨论】:

  • @GOTO0 “它确实缩小了”是什么意思? callback 接受 OneTwo 作为参数,这是 OP 想要阻止的。他们希望根据检查 arg === "Foo"callback 缩小为仅接受 One
  • @GOTO0 不是真的,你在callback("Two") 调用和callback("One") 上都会收到错误。因此,如果没有类型断言,您将无法使用任何东西调用它。我猜这过于简化了,所以不确定这是否有助于实际用例
  • @GOTO0 我应该写到您提出的解决方案是我最初的尝试。正如 Titian 所提到的,这根本不会缩小类型,并使所有对 callback 的调用无效。
  • @HenrikAndersson 是的,这是真的。现在要删除我的 cmets。

标签: typescript typeof narrowing conditional-types type-narrowing


【解决方案1】:

缩小适用于单个变量。 Typescript 没有可以一起缩小的相关变量/参数的概念。

你唯一能做的就是使用一个可区分的元组作为参数,缩小元组并从中传播参数:

type Argument = "Foo" | "Bar";
type CallbackArgument<T extends Argument> = T extends "Foo" ? "One" : T extends "Bar" ? "Two" : never;

function foo(args: ["Foo", (p: "One") => void] |  ["Bar", (p: "Two") => void]) {
    if (args[0] === "Foo") {
        const [arg, callback] = args;
        callback("Two") // Error now
        const two: CallbackArgument<typeof arg> = "Two" // Error
    }
}

【讨论】:

  • 这很不幸。尽管这不能解决我的问题,但我认为有一些方法可以通过定义具有属性argument: Tcallback(arg: CallbackArgument&lt;T&gt;): void 的可区分联合来使这个策略更好。然后可以定义foo(args: ArgUnion&lt;"Foo"&gt; | ArgUnion&lt;"Bar"&gt;) 和模式匹配以获得回调的正确缩小范围。这避免了解构,同时也强制正确的回调参数。
  • @HenrikAndersson 是的,使用对象而不是元组更适合实现,但这会改变调用站点。
猜你喜欢
  • 2021-05-25
  • 1970-01-01
  • 2022-10-04
  • 2020-03-20
  • 1970-01-01
  • 2016-05-24
  • 1970-01-01
  • 2021-03-04
  • 1970-01-01
相关资源
最近更新 更多