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

Conditional type based on boolean flag in an object

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

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

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

一旦执行此操作,由于 log() 的 return 类型本身将是通用的,编译器将无法验证实现中的特定 return 值匹配它,即使您正在检查 opts.two。这是一个已知问题,控制流分析(如检查 opts.two)不会缩小泛型类型参数的类型(请参阅 microsoft/TypeScript#24085 and/or microsoft/TypeScript#13995). Since the compiler will be unable to verify something that you know, it's reasonable to use a type assertion to suppress compiler warnings. Or, you can do the equivalent of an assertion: give the function a single 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