【问题标题】:Typescript can't find property even though declared即使声明了 Typescript 也找不到属性
【发布时间】:2020-06-09 14:13:06
【问题描述】:

我正在尝试修改我正在使用的包的类型声明,因为这些类型已过时。我可以很好地调整大多数东西,但有一个错误我似乎无法修复,那就是从函数返回的接口属性。

这是一个代码示例。我已经删除了我很确定与问题无关的代码,以使其更容易阅读。如果您觉得缺少一些可能有助于理解问题的内容,请告诉我。

declare namespace renderContent {
  type renderContentCallback = (
    eventOrImage: Event | HTMLCanvasElement | HTMLImageElement,
    data?: MetaData
  ) => void;

  interface ImageHead {
    imageHead?: ArrayBuffer | Uint8Array;
  }

  interface MetaData extends ImageHead {
    //...
  }

  interface PromiseData extends MetaData {
    image: HTMLCanvasElement | HTMLImageElement;
  }

  interface BasicOptions {
    //...
  }

  type renderContentOptions = BasicOptions
}

interface renderContent {
  (
    file: File | Blob | string,
    /* If options are defined instead of a callback function, the next argument will be skipped and the function will return a promise */
    callbackOrOption?: renderContent.renderContentCallback | renderContent.renderContentOptions,
    options?: renderContent.renderContentOptions
  ): HTMLImageElement | FileReader | Promise<renderContent.PromiseData> | false;
}

declare const renderContent: renderContent;

export = renderContent;

当我像这样在我的代码中使用它时:

const { image } = await renderContent(file, options);

我收到一条错误消息:

类型“假”上不存在属性“图像”| HTMLImageElement | 文件阅读器 | PromiseData'

对我来说,它似乎在 PromiseData 接口中明确定义,并在错误消息中列出。那我错过了什么?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    问题是,打字稿不能确定renderContent 返回的类型是Promise&lt;renderContent.PromiseData&gt;。实际上,对于 typescript,类型也可以是 FileReaderHtMLImageElement...

    这些类型没有密钥 image,这就是您收到此错误的原因。

    我想你认为使用关键字await 会使打字稿猜测Promise&lt;renderContent.PromiseData&gt; 是正确的返回类型,但不幸的是不是。


    为了告诉 typescript 你在等什么,使用关键字as,比如:

    playground

    const { image } = await (renderContent(_) as Promise<renderContent.PromiseData>);
    

    关于&解决方案:

    它可能有效,但有副作用。例如:

    如果你使用这样的功能:

     const data = renderContent(false as unknown as File);
    

    Typescript 将允许您调用data.readAsArrayBufferdata.height 一样多,这是错误的。因为renderContent 要么返回HTMLCanvasElement,要么返回HTMLImageElement


    一个新的解决方案!有一点返工:​​3>

    综合考虑,更好的解决方案是准确描述renderContent 调用场景的样子:

    type possibilityA = (
        file: File | Blob | string,
        options: renderContent.renderContentOptions
    ) => Promise<renderContent.PromiseData>;
    
    type possibilityB = (
        file: File | Blob | string,
        callback: renderContent.renderContentCallback,
    ) => HTMLImageElement | FileReader | false;
    
    type possibilityC = (
        file: File | Blob | string,
        callback: renderContent.renderContentCallback,
        options: renderContent.renderContentOptions
    ) => HTMLImageElement | FileReader | false;
    
    type renderContent = possibilityA & possibilityB & possibilityC;
    
    declare const renderContent: renderContent;
    
    (async() => {
      const data = await renderContent('foo', {  });
    })();
    

    playground

    在此解决方案中,typescript 将根据您提供的参数选择适当的重载,该参数将相应地定义 data 的类型。

    【讨论】:

    • 谢谢 Grégory,这似乎可行,我能够让它与 const { image } = await (renderContent(file as File, option) as Promise&lt;renderContent.PromiseData&gt;); 一起工作但我也刚刚发现我可以通过更改预期来解决问题,而无需修改函数返回HTMLImageElement &amp; FileReader &amp; Promise&lt;renderContent.PromiseData&gt; &amp; false。可能有不推荐方法的原因吗?
    • @EinarÓlafsson 我已经编辑了我的帖子,其中包含对您的回答以及处理所有案件的另一个解决方案! (良好的自动完成和类型检查)
    • 该解决方案效果很好,非常感谢您对实现的解释,非常有帮助!
    猜你喜欢
    • 2013-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-03
    • 2019-06-20
    • 1970-01-01
    • 1970-01-01
    • 2016-09-20
    相关资源
    最近更新 更多