基于对象中布尔标志的条件类型
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}
始终为真,如果我玩弄它,空对象和假值将按它们应有的方式运行。但它不适用于我需要的通用变量。
我不是 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")
我想键入一个 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}
始终为真,如果我玩弄它,空对象和假值将按它们应有的方式运行。但它不适用于我需要的通用变量。
我不是 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")