在 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;
}
};
});
