網站搜尋

在 TypeScript 中存取已宣告(且未匯出)的模組中的列舉,在轉譯後


簡要

在不導出命名空間,而僅宣告它的情況下,我想訪問其中的枚舉。一旦轉譯後,該命名空間無法訪問(它是未定義的對象),因此無法訪問枚舉(命名空間會出錯)。

解釋

我正在設定一個 TypeScript 專案,我想避免匯入所有專案特定的定義。在這個程式片段中,我建立了一個命名空間,將我關於 redux 邏輯的本地定義放在裡面。

redux 宣告的摘錄 (redux.d.ts):

declare namespace Redux {

    namespace Actions {

        enum TypeKeys {
            REDUX_INIT          = '@@redux/INIT',
            BOOTSTRAP           = 'BOOTSTRAP',
            CORPUS_LOADED       = 'CORPUS_LOADED',
            ...
        }
        ...
        interface Definition {
            doSomething(): void;
            ...
        }
        ...
    }

    namespace Store {
        interface Definition { ... }
        ...
    }


}

這讓我可以避免在所有程式碼中匯入定義(例如在我的頂層組件中 `const mapStateToProps=(state: Redux.Store.Definition) => state;`,我不必匯入任何 Redux 命名空間)。

進入問題,我寫了這個 reducer(corpusReducer.ts 節選):

// Note: no need to import Redux!
export default (state: Redux.Store.CorpusManagement, action: Redux.Actions.ActionTypes): Redux.Store.CorpusManagement => {

    switch (action.type) {

        case Redux.Actions.TypeKeys.CORPUS_LOADED:
            return { corpus: action.payload };

        default:
            return state === undefined ? { corpus: { documents: [] } } : state;

    }

};

一旦轉譯後,我在這一行 case Redux.Actions.TypeKeys.CORPUS_LOADED 收到錯誤 ReferenceError: Redux is not defined。

我懷疑 Redux 在執行時不可用,因為它屬於 TypeScript 的命名空間。另一方面,TypeKeys 是一個列舉(enum),應該在 JavaScript 中有對應的映射,因此我預期的行為是該列舉在轉譯後仍然可以存取。

如果我添加一個 export(將該行改成 declare namespace Redux),該文件就會被“提升”為模組,但那麼我就需要在程式碼中的每個地方都進行導入。

問題

我如何在不使用 export 子句的情況下訪問 enum(因此避免在每處明確導入模塊)?是否有我沒有遵循的好做法?

答案#1

列舉使用 enum 物件在運行時保存值。這意味著如果這個物件無法訪問,依賴其存在的程式碼就會像你發現的那樣失敗。

你最好的解決辦法是確保該對象已被導出。新增一個包含該枚舉的模組,然後導入它。

如果這不可能,你可以在宣告中使用 const enum。Const enum 不依賴於對象的存在,因為每當編譯器遇到對 enum 值的引用時,實際值將被硬編碼到生成的 JS 中。

const enum TypeKeys { // No code for const enums is generated.
    REDUX_INIT = '@@redux/INIT',
    BOOTSTRAP = 'BOOTSTRAP',
    CORPUS_LOADED = 'CORPUS_LOADED',
}


let d = TypeKeys.BOOTSTRAP; // All enum references are replaced with values, so this becomes var d = "BOOTSTRAP"

你應該對這種方法小心,因為這意味著所有值都被硬編碼在你的 JS 中,對枚舉的任何更改都將需要你重新構建引用該枚舉的任何代碼。

編輯

對我有效的完整程式碼:

// definitions.d.ts
declare namespace Redux {

    namespace Actions {

        const enum TypeKeys {
            REDUX_INIT          = '@@redux/INIT',
            BOOTSTRAP           = 'BOOTSTRAP',
            CORPUS_LOADED       = 'CORPUS_LOADED',

        }
        interface Definition {
            doSomething(): void;
        }
        interface ActionTypes {
            type: TypeKeys
            payload: any;
        }
    }

    namespace Store {
        class CorpusManagement {}
    }
}

// usage.ts
/// <reference path="./definitions.d.ts" />
export default (state: Redux.Store.CorpusManagement, action: Redux.Actions.ActionTypes): Redux.Store.CorpusManagement => {

    switch (action.type) {

        case Redux.Actions.TypeKeys.CORPUS_LOADED:
            return { corpus: action.payload };

        default:
            return state === undefined ? { corpus: { documents: [] } } : state;

    }

};

輸出:

(...)
(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    /// <reference path="./definitions.d.ts" />
    exports.default = (state, action) => {
        switch (action.type) {
            case "CORPUS_LOADED" /* CORPUS_LOADED */:
                return { corpus: action.payload };
            default:
                return state === undefined ? { corpus: { documents: [] } } : state;
        }
    };
});