typescript 泛型约束的问题

type Resource<T extends HTMLAudioElement | HTMLImageElement> = {
 id: string
 resource: T
}

function resourceLoad<T extends HTMLAudioElement | HTMLImageElement>(
 id: string,
 path: string,
 type: "img" | "audio"
): Promise<Resource<T>> {
 return new Promise((resolove, reject) => {
 let resource:T
 if (type == "audio") {
 resource = new Audio(path)
 resource.oncanplaythrough = () => {

 resolove({ id, resource:resource as HTMLAudioElement})
      }
    } 
 else {
 resource = new window.Image()
 resource.src = path
 resource.onload = () => {
 resolove({ id, resource :resource as HTMLImageElement})
      }
    }
 resource.onerror = () => {
 reject(`load resource:"${id}"fail.\npath:${path}`)
    }
  })
}

问题是在resource = new Audio(path) 这一部分会提示以下

resource 错误,Type 'HTMLAudioElement' is not assignable to type 'T'.

'HTMLAudioElement' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'HTMLAudioElement | HTMLImageElement'.ts(2322)

实现功能大概就是传入type = 'img' 返回 HTMLImageElement ,传入 type = 'audio' 返回 HTMLAudioElement。

如何让他取消错误提示?

目前用起来提示是正常的。
image.png

阅读 2.4k
1 个回答

你这里的范型没什么必要,你都知道了只有两种类型,
我这样写是不是清晰了很多

type AudioOrImage = HTMLAudioElement | HTMLImageElement
function resourceLoad(id: string, path: string, type: "img" | "audio") {
  return new Promise<{id: string; resource: AudioOrImage}>((resolve, reject) => {
    let resource: AudioOrImage
    if(type === 'audio'){
      resource = new Audio(path)
      resource.oncanplaythrough = () => resolve({resource, id})
    }else{
      resource = new window.Image()
      resource.src = path
      resource.onload = () => {
        resolve({ id, resource :resource})
      }
    }
    resource.onerror = () => {
      reject(`load resource:"${id}"fail.\npath:${path}`)
    }
  })
}

修改版 使用了函数重载,我还是既然觉得固定了类型范围就不应该使用泛型

type Resource < T > = {
    id: string
    resource: T
}

function resourceLoad(id: string, path: string, type: "img"): Promise < Resource < HTMLImageElement >> ;

function resourceLoad(id: string, path: string, type: "audio"): Promise < Resource < HTMLAudioElement >> ;

function resourceLoad(id: string, path: string, type: any): any {
    return new Promise((resolove, reject) => {
        let resource!: HTMLAudioElement | HTMLImageElement
        if (type == "audio") {
            resource = new Audio(path)
            resource.oncanplaythrough = () => {
                resolove({
                    id,
                    resource: resource
                })
            }
        } else {
            resource = new window.Image()
            resource.src = path
            resource.onload = () => {
                resolove({
                    id,
                    resource: resource
                })
            }
        }
        resource.onerror = () => {
            reject(`load resource:"${id}"fail.\npath:${path}`)
        }
    })
}

let a = resourceLoad('1', '1', 'img')
let b = resourceLoad('1', '1', 'audio')
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进