我想键入一个返回两种不同类型之一的函数,具体取决于是否在选项对象中传递了标志。
这是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}
始终为真,如果我玩弄那个空对象和一个假值,它们将按照其应有的方式工作。但这当然不适用于我需要的通用变量。
答案 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
好的,希望能有所帮助;祝你好运!