我想输入一个函数,它返回两种不同类型中的一种,这取决于是否在options对象中传递了标志。
下面是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}
总是真的,如果我使用它,空对象和假的值就会表现得像它们应该的那样。但它不能与泛型变量一起工作,这当然是我需要的。
发布于 2019-11-27 01:43:47
我不能100%确定您的用例是什么,但是您代码的问题是,如果您希望输出的类型取决于输入的类型,那么log()
需要是一个generic函数。因此,签名需要看起来像log<O extends Opts>(opts?: O): LogFn<O>
;
执行此操作后,由于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")
https://stackoverflow.com/questions/59060034
复制相似问题