Search code examples
javascriptreactjstypescriptes6-promisees6-modules

Why does typescript infer a default prop when we dynamically import a javascript file which does not have a default export


I am dynamically importing a javascript file that exports multiple functions (and does not have a default export) const sayHi = import('./sayHi.js')

I am expecting sayHi's type to be Promise<{name1: function, name2: function}> but it is Promise<{default: typeof import('./sayHi.js'), name1: function, name2: function}>

why does the default prop get added

Here is a codesandbox https://codesandbox.io/s/typescript-dynamic-import-w4nb1?file=/src/index.tsx

why does __promise__ have a default prop automatically added? enter image description here


Solution

  • Synthetic Default Exports

    There is an option in your tsconfig file called allowSyntheticDefaults which allows typescript to support Babel's creation of default exports for files which do not have an explicit default. The default object is a keyed object containing all of the named exports from that file. If this setting is true then all files will have an export called default included alongside their named exports.

    Typing makeDefaultExport

    Your function makeDefaultExport only uses one specific named export from the file, so we can apply stricter typescipt typings to this function and get a more specific return. Instead of just keyof T, we use a second generic K for the key.

    export const makeDefaultExport = <T, K extends keyof T>(
      promise: Promise<T>, key: K
    ) => {
    

    Now this function will return only the type for the specific named export instead of the union of all exports (which was including the default). The return type is:

    Promise<{ default: T[K]; }>
    

    Edit: I recommend the above approach as it properly handles files where not all named exports have the same type. In your particular case all of the exports are the same type, so we won't get any type widening by returning a union of all export types except the default.

    export const makeDefaultExport = <T>(
      promise: Promise<T>, key: Exclude<keyof T, "default">
    ) => {