Search code examples
typescriptcordova-pluginstypescript-typings

How to create a new Promise with child type B when the function I want to call expects parent type A


I'm currently adding types in our Angular project, using Cordova file plugin. In order to list all the directories at a specific path, we use the following code :

const directory: any = await new Promise((resolve, reject) => {
      window.resolveLocalFileSystemURL(cordova.file.dataDirectory + path, resolve, reject);
});

I know that directory is a DirectoryEntry object, which is an interface extending Entry. My problem is that using const directory: DirectoryEntry makes TypeScript complain.

Impossible to assign type 'Entry' to type 'DirectoryEntry | PromiseLike<DirectoryEntry>'.

How can I properly do this ? I know that I could simply let directory be any and cast it latter, but still, I'm curious to see how this problem could be solved.

Thanks a lot for all your answers.


Solution

  • You should be able to pass the type parameter to the promise constructor - like so:

    const directory = await new Promise<DirectoryEntry>((resolve, reject) => {
      window.resolveLocalFileSystemURL(cordova.file.dataDirectory + path, resolve, reject);
    });
    

    Edit: Ok - I see your problem is that resolveLocalFileSystemURL is telling you that the callback passed to it's second parameter is going to be called with Entry, not DataDirectory.

    Because resolveLocalFileSystemURL will pass an object to resolve, it can't tell if it's a directory or a file (and - your file structure might change).

    If you want to ignore the warning, I'd say something like:

    const entry = await new Promise<Entry>((resolve, reject) => {
      window.resolveLocalFileSystemURL(cordova.file.dataDirectory + path, resolve, reject);
    });
    
    // We know this path is a directory
    const directory = entry as DirectoryEntry;
    

    or

    const directory = await new Promise<DirectoryEntry>((resolve, reject) => {
      window.resolveLocalFileSystemURL(cordova.file.dataDirectory + path, entry => resolve(entry as DirectoryEntry), reject);
    });
    

    A safer alternative is to actually check it is a directory, and handle as appropriate, using a type-predicate:

    function isDirectory(entry: Entry): entry is DirectoryEntry {
      return entry.isDirectory;
    }
    
    const entry = await new Promise<Entry>((resolve, reject) => {
      window.resolveLocalFileSystemURL(cordova.file.dataDirectory + path, resolve, reject);
    });
    
    if (isDirectory(entry)) {
      // In this scope, TS has now narrowed entry to DirectoryEntry safely 
    }