1
votes

My issue arises from attempting to add a TypeScript type definition .d.ts to a 3rd party library. Keep in mind I do not control this library, I am simply trying to consume it and appease the tsc compiler.

Reading the guidelines here re: writing d.ts files:

https://typescript.codeplex.com/wikipage?title=Writing%20Definition%20%28.d.ts%29%20Files

I am looking at this specific example: New + callable methods

Here, the documentation shows the following pattern:

interface Widget {
    sprock(): void;
}

interface WidgetFactory {
    new(name: string): Widget;
    (width: number, height: number): Widget;
}

declare var widget: WidgetFactory;

Building on this I have created the following for my needs:

declare module "react-localization" {
    interface LocalizedStringsMethods {
        setLanguage(language: string): void;
        getLanguage(): string;
        getInterfaceLanguage(): string;
        formatString(str: string, ...values: any[]): string
        getAvailableLanguages(): string[]
    }

    interface LocalizedStringsFactory {
        new (props: any): LocalizedStringsMethods;
    }

    var LocalizedStrings: LocalizedStringsFactory;

    export default LocalizedStrings;
}

The notable exception here is the documentation version does not use declare module. Aside from that, to my eye, these SHOULD be the same an yield a similar result; I am clearly missing something. I get the following error instead:

error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.

I can change my definition to this:

declare module "react-localization" {
    var LocalizedStrings: any;
    export = LocalizedStrings;
}

The error is now gone, but I lose all the wonderful type information. =,(

I am at a loss. What exactly am doing wrong here?

Ok to add to the mystery, I tried doing export = in the fuller type example instead of export default:

declare module "react-localization" {
    interface LocalizedStringsMethods {
        setLanguage(language: string): void;
        getLanguage(): string;
        getInterfaceLanguage(): string;
        formatString(str: string, ...values: any[]): string
        getAvailableLanguages(): string[]
    }

    interface LocalizedStringsFactory {
        new (props: any): LocalizedStringsMethods;
    }

    var LocalizedStrings: LocalizedStringsFactory;

    export = LocalizedStrings; // <----- HERE instead of export default
}

Can someone explain to me why this works but export default ends up returning undefined? Also, I have to import this like:

import * as LocalizedStrings from 'react-localization' I'd prefer to do the import as import LocalizedStrings from 'react-localization. At this point I'll live with the former, I'd just like to understand why.

1
additionally it looks like it's relevant that this 3rd party lib is compiled from babel: github.com/Microsoft/TypeScript/issues/5565 When Babel transpiles a module whose only export is an export default it injects a module.exports = exports["default"]; into the generated code, causing the exported object to become the function itself (instead of a module object that has the function as the default property).AJ Venturella

1 Answers

1
votes

Solution is:

react-localization.d.ts

declare module "react-localization" {
    interface LocalizedStringsMethods {
        setLanguage(language: string): void;
        getLanguage(): string;
        getInterfaceLanguage(): string;
        formatString(str: string, ...values: any[]): string
        getAvailableLanguages(): string[]
    }

    interface LocalizedStringsFactory {
        new (props: any): LocalizedStringsMethods;
    }

    var LocalizedStrings: LocalizedStringsFactory;

    export default LocalizedStrings;
}

yourFile.ts

import LocalizedStrings from 'react-localization';

var x = new LocalizedStrings(123);

If you want to use export = then import = require() should be used. More details in https://www.typescriptlang.org/docs/handbook/modules.html