提问人:Mosti 提问时间:11/2/2023 最后编辑:T.J. CrowderMosti 更新时间:11/2/2023 访问量:68
带有布尔检查的 TypeScript 上的条件返回类型似乎不起作用
Conditional return type on TypeScript with boolean check doesn't seem to work
问:
给定以下代码:
type OpenFileDialogReturn<TMulti extends boolean> =
TMulti extends true ? FileList | null | undefined :
TMulti extends false ? File | null | undefined :
never
type OpenFileDialog = {
(options: { multiple?: false, acceptedFileTypes?: string }): Promise<OpenFileDialogReturn<false>>
(options: { multiple: true, acceptedFileTypes?: string }): Promise<OpenFileDialogReturn<true>>
}
export const openFileDialog: OpenFileDialog = (options = { }) => {
return new Promise(resolve => {
const input = document.createElement('input')
input.type = 'file'
input.style.opacity = '0'
if (options.acceptedFileTypes) input.accept = options.acceptedFileTypes
if (options.multiple) input.multiple = true
input.addEventListener('input', () => {
if (options && options.multiple === true) resolve(input.files)
const file = input.files?.[0]
if (!file) resolve(null)
resolve(file)
})
input.click()
})
}
TypeScript 在最后一个抛出错误resolve(file)
Argument of type 'File | undefined' is not assignable to parameter of type 'FileList | PromiseLike<FileList | null | undefined> | null | undefined'.
Type 'File' is not assignable to type 'FileList | PromiseLike<FileList | null | undefined> | null | undefined'.ts(2345)
const file: File | undefined
我尝试了几种方法,甚至将所有内容包装在严格的 if/else 块中,以确保 TS 理解此时是严格的,但似乎没有任何效果。任何帮助都是值得赞赏的。options.multiple
false
答:
由于您没有在函数中创建的 类型参数,TypeScript 似乎正在选择您的重载之一(那个),因此它拒绝了使用 .(我不确定那里的分辨率的细节,但它似乎与重载的顺序有关;我不会依赖它。然后,由于其返回类型,实现函数与该类型不兼容。Promise
FileList | null | undefined
resolve
File
OpenFileDialog
比我更好的 TypeScript 负责人告诉我,重载函数的实现与其所有重载签名不严格兼容并不罕见——也就是说,实现不是完全类型安全的。在这种情况下,您可以键入 promise as,以便至少 promise 执行器中的代码被限制为返回其中之一(即使从技术上讲,编码错误可能会让它在应该返回 a 时返回 a ),但随后对结果 (, ugh) 使用类型断言使实现可分配给 。(或者,您可以在一开始就使用 any 作为类型参数,但随后执行器中的代码是完全不受约束的。File | FileList | null | undefined
FileList
File
as Promise<any>
OpenFileDialog
您还需要进行一个不相关的小更改:在 中的签名中标记为可选,因为您的实现为其提供了默认值。options
false
OpenFileDialog
像这样的东西:
type OpenFileDialogReturn<TMulti extends boolean> =
TMulti extends true ? FileList | null | undefined :
TMulti extends false ? File | null | undefined :
never
type OpenFileDialog = {
(options?: { multiple?: false, acceptedFileTypes?: string }): Promise<OpenFileDialogReturn<false>>
// ^−−−−−−−−−−− make `options` optional in this signature
(options: { multiple: true, acceptedFileTypes?: string }): Promise<OpenFileDialogReturn<true>>
}
export const openFileDialog: OpenFileDialog = (options = { }) => {
return new Promise<File | FileList | null | undefined>(resolve => {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
const input = document.createElement('input')
input.type = 'file'
input.style.opacity = '0'
if (options.acceptedFileTypes) input.accept = options.acceptedFileTypes
if (options.multiple) input.multiple = true
input.addEventListener('input', () => {
if (options && options.multiple === true) resolve(input.files)
const file = input.files?.[0]
if (!file) resolve(null)
resolve(file)
})
input.click()
}) as Promise<any>;
// ^^^^^^^^^^^^^^^−−−−− to make the implementation compatible with the overloads (ugh)
}
检查使用情况:
const x = await openFileDialog({multiple: true});
// ^? −−− const x: FileList | null | undefined
const y = await openFileDialog();
// ^? −−− const y: File | null | undefined
const z = await openFileDialog({multiple: false});
// ^? −−− const z: File | null | undefined
评论
resolve(file)
即使 是 ,也会执行。你需要一个声明。但实际问题似乎是似乎并不期望收到.if (file)
true
else
resolve
File
resolve(file)
File
FileList
options.multiple
Promise
resolve(file)
if(!file) resolve(null)
resolve
null
undefined
if (!file) resolve(null)