基于对象中布尔标志的条件类型

时间:2019-11-26 22:03:56

标签: typescript typescript-typings typescript-generics

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

这是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

1 个答案:

答案 0 :(得分:1)

我不是100%知道您的用例是什么,但是您的代码存在的问题是,如果您希望输出的类型为log()必须是generic函数取决于输入的类型。因此签名需要看起来像log<O extends Opts>(opts?: O): LogFn<O>;

一旦执行此操作,由于log()的返回类型本身将是通用的,因此即使您正在检查{{},编译器也将无法验证实现中的特定返回值是否与之匹配。 1}}。 known issue控制流分析(例如检查opts.two)不会缩小泛型变量的类型。由于编译器将无法验证您知道的内容,因此使用type assertion来禁止编译器警告是合理的。或者,您可以执行与声明相同的操作:为函数提供单个overload签名,并使实现签名足够宽以防止错误。

它看起来像这样:

opts.two

该编译没有错误。请注意,我已经将参数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

好的,希望能有所帮助;祝你好运!

Link to code