【问题标题】:Access an enum in a declared (and not exported) module in TypeScript once transpiled转译后访问 TypeScript 中已声明(而非导出)模块中的枚举
【发布时间】:2018-10-15 22:22:49
【问题描述】:

TL;DR

不导出命名空间,只声明它,我想访问其中的枚举。转译后,命名空间不可访问(它是一个未定义的对象),因此无法访问枚举(命名空间出错)。

说明

我正在 TypesScript 中设置一个项目,并且我想避免导入特定于该项目的所有定义。在这个 sn-p 中,我创建了一个命名空间,用于放置我的 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;

    }

};

转译后,我收到错误 ReferenceError: Redux is not defined 对于行 case Redux.Actions.TypeKeys.CORPUS_LOADED

我怀疑Redux 在运行时不可用,因为命名空间属于 TypeScript 世界。 TypeKeys,另一方面,它是一个枚举,它应该在 JavaScript 中有一个映射,因此我的预期行为是该枚举在转译后仍然可以访问。

如果我添加一个导出(将行更改为declare namespace Redux),该文件被“提升”为一个模块,但是我需要在我的代码中的任何地方导入它。

问题

如何避免使用 export 子句访问枚举(因此要在任何地方显式导入模块)?有什么好的做法我没有遵循吗?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    枚举使用枚举对象在运行时保存值。这意味着,如果无法访问此对象,则依赖于它的存在的代码将会失败,正如您所发现的那样。

    您最好的解决方案是确保该对象已导出。添加一个额外的模块来保存枚举并导入它。

    如果这不可能,您可以在声明中使用 const 枚举。常量枚举不依赖于对象的存在,因为每当编译器遇到对枚举值的引用时,实际值将被硬编码在生成的 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;
            }
        };
    });
    

    【讨论】:

    • 我可以满足重建要求(这个声明对于这个项目来说是本地的并且是自包含的),但是“const”并不能解决问题。 :( 此外,如果我添加一个“import”子句,该文件将成为一个适当的模块,然后让我回到我想避免的事情:在我的所有项目文件中导入所有声明
    • @pierpytom 它应该可以在没有导入且没有在命名空间中导出的情况下工作。你是什​​么意思const 不能解决问题。访问枚举的a成员时会生成什么JS?
    • 这就是 js 的样子:function(e, t, n) { "use strict"; Object.defineProperty(t, "__esModule", { value: !0 }), t.default = function(e, t) { switch (t.type) { case Redux.Actions.TypeKeys.CORPUS_LOADED: return { corpus: t.payload }; default: return void 0 === e ? { corpus: { documents: [] } } : e } } } 枚举是一个常量,但保存在命名空间内。
    • 出于完整性考虑,问题似乎来自“declare”关键字。如果我执行declare namespace Foo { const enum Bar { Hello = 'World' } },我可以重复错误,然后我尝试console.log(Foo.Bar.Hello);,即使在同一个文件中(就在导出默认值之后)。
    • 我发现这个bug是在指定“transpileOnly”选项时出现的,并且在“create-react-app-ts”的许多依赖项中设置了几个标志,这也是很多检查。我将转向香草 webpack 看看会发生什么,但我不明白为什么你的解决方案不起作用,谢谢! :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-06
    • 1970-01-01
    • 2013-08-21
    • 2018-03-07
    • 1970-01-01
    • 2021-11-04
    • 2023-02-22
    相关资源
    最近更新 更多