TypeScriptのd.tsでCommonJSスタイルのエクスポートの型を記述する際に引っかかったこと


CommonJS対応プルリク

https://bitbucket.org/atlassian/atlassian-connect-express/pull-requests/254
https://bitbucket.org/atlassian/atlassian-connect-express/pull-requests/257

d.tsコードの全貌

type BearerToken = {
    access_token: string;
};

declare class AddOn {}
declare class HostClient {
    getUserBearerToken: (scopes: string[], clientSettings: AddOnFactory.ClientInfo) => Promise<BearerToken>;
}

declare function AddOnFactory(): AddOn;

declare namespace AddOnFactory {
    export type HostClient = InstanceType<typeof HostClient>;
    export interface ClientInfo {
        key: string
    }
    export type AddOn = InstanceType<typeof AddOn>;
    export type AddOnFactory = typeof AddOnFactory;
    export { BearerToken };
}

export = AddOnFactory;

使い方

esModuleInteropfalseでも動きます。

import ace = require('atlassian-connect-express');
import { HostClient, ClientInfo, AddOn, AddOnFactory, BearerToken } from 'atlassian-connect-express';

説明

今回求められたのは、module.exportsで関数をエクスポートしつつ、名前付きエクスポートで型をエクスポートすることです。
これを実現するためには、functionnamespaceを同名で宣言して、export = AddOnFactory構文でエクスポートする必要があります。
また、エクスポートするインターフェースはnamespace内で宣言すると何回も別名を定義する必要がなく、簡潔です。
typeof AddOnFactorytypeof付けないと循環参照になってエラーになります。
最後に、typeof HostClientと書くと型が"function"になってしまうのでInstanceType<typeof HostClient>と書く必要があります。
これ知らなくて知らない人から怒られました:frowning2: