【问题标题】:Conditional type based on boolean flag in an object基于对象中的布尔标志的条件类型
【发布时间】:2020-03-22 09:49:41
【问题描述】:

我想键入一个返回两种不同类型之一的函数,具体取决于是否在选项对象中传递了标志。

这是 TypeScript 抱怨的尝试:

type Opts = { two?: boolean }

type LogFn<O> = O extends { two?: true }
   ? (a: string, b: string) => any
   : (a: string) => any

   function log(opts: Opts = {}): LogFn<Opts> {
      if (opts.two) {
      // Type '(a: any, b: any) => void' is not assignable to type
      // '(a: string) => any'.(2322)
      return (a, b) => { console.log("two: ", a, b) }
   } else {
      return (a) => { console.log("one: ", a) }
   }
}

const one = log()
const two = log({ two: true })

one("x")
// Expected 1 arguments, but got 2.(2554)
two("y", "z")

似乎O extends { two?: true} 表达式总是错误的。但是表达式{ two: true} extends { two?: true} 始终为真,如果我玩弄空对象和假值,则它们的行为应该如此。但它不适用于我需要的泛型变量。

TypeScript Playground link for the above example

【问题讨论】:

  • 我建议不要将类型参数命名为与其他类型相同。
  • @kaya3 谢谢!我已经更新了我的代码示例,以不同的方式命名它,同时也与下面的答案保持一致。

标签: typescript typescript-typings typescript-generics


【解决方案1】:

我不能 100% 确定您的用例是什么,但您的代码的问题是 log() 需要是 generic 函数,如果您希望输出的类型取决于类型的输入。所以签名需要看起来像log&lt;O extends Opts&gt;(opts?: O): LogFn&lt;O&gt;;

一旦执行此操作,由于log() 的返回类型本身将是通用的,编译器将无法验证实现中的特定返回值是否匹配它,即使您正在检查opts.two。这是一个已知问题,即控制流分析(如检查opts.two)不会缩小泛型类型参数的类型(请参阅microsoft/TypeScript#24085 和/或microsoft/TypeScript#13995)。由于编译器将无法验证您知道的内容,因此使用type assertion 来抑制编译器警告是合理的。或者,您可以执行与断言等效的操作:给函数一个 overload 签名,并让实现签名足够宽以防止错误。

看起来像这样:

function log<O extends Opts = Opts>(_opts?: O): LogFn<O>;
function log(_opts?: Opts): (a: string, b?: string) => any {
   const opts: Opts = _opts || {}
   if (opts.two) {
      return (a, b) => { console.log("two: ", a, b) }
   } else {
      return (a) => { console.log("one: ", a) }
   }
}

编译没有错误。请注意,我已经创建了参数_opts,它要么是O,要么是缺失的(因此是undefined),在它缺失的情况下,编译器将回退到Opts 的通用默认值。然后在函数内部我创建const opts: Opts = _opts || {} 以确保它总是被定义。这应该与原始默认函数参数的行为相同,但我的代码使用通用 O 而不是具体的 Opts 会更好地发挥作用。

无论如何,现在下面的行为符合您的预期:

const one = log()
const two = log({ two: true })

one("x")
two("y", "z")

Link to code

【讨论】:

  • 很棒的答案! O extends Opts = Opts 是做什么的? generic default of Opts 是什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-18
相关资源
最近更新 更多