Reputation: 3177
I'm trying to modify a type declaration for a package that I'm using since the types are outdated. I'm able to adjust most things fine, but there is one error I don't seem to be able to fix, and that is that a property of an interface that is returned from the function.
Here is a code sample. I have stripped out code I am pretty sure are not related to the issue, to make reading it easier. If you feel there is something missing that might help understand the problem, please let me know.
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;
When I then use this in my code like so:
const { image } = await renderContent(file, options);
I get an error saying:
Property 'image' does not exist on type 'false | HTMLImageElement | FileReader | PromiseData'
To me, it seems it is clearly defined in the PromiseData interface, listed in the error message. So what am I missing?
Upvotes: 2
Views: 1217
Reputation: 23495
The thing is, typescript cannot know for sure that the type returned by renderContent
will be Promise<renderContent.PromiseData>
. In fact, for typescript the type could also be FileReader
or HtMLImageElement
...
Theses types doesn't have the key image
, and this is why you got this error.
I suppose that you think that using the keyword await
would make typescript guess that Promise<renderContent.PromiseData>
is the correct returned type, but unfortunately no.
In order to tell typescript what you are waiting for, use the keyword as
, like :
const { image } = await (renderContent(_) as Promise<renderContent.PromiseData>);
It may work, but with side effects. As example :
If you use the function like :
const data = renderContent(false as unknown as File);
Typescript will allow you to call data.readAsArrayBuffer
as much as data.height
, which is wrong. Because renderContent
returns either a HTMLCanvasElement
, either a HTMLImageElement
.
All considered, a better solution would be to exactly describe what the renderContent
call scenarios looks like :
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', { });
})();
In this soluce, typescript will pick the appropriate overload depending on the parameter you provide which will define the type of data
accordingly.
Upvotes: 3