【问题标题】:Typescript - How to declare anonymous object with known properties?Typescript - 如何声明具有已知属性的匿名对象?
【发布时间】:2018-07-04 01:18:28
【问题描述】:

在 JS 中,我的很多模块只是包装静态函数、枚举和属性的对象。例如,我有一个模块Debug 与此类似(我确实简化了它):

export default Debug = {
    // Current mode set, enum value from Modes. Setted outside of the module (see below).
    mode : null,

    // Enum of all possible modes
    Modes : {
        DEV : 0,
        PROD : 1,
    },

    // getter, checks if it's in production mode
    get isInProdMode() {
        return Debug.mode === Debug.Modes.PROD;
    },

    // If the condition is met, it will throw an error in development mode, or just silently log a warning in production mode
    assert : function(condition, message){
        if (condiftion) {
            if (Debug.isInProdMode)
                console.warn(message);
            else
                throw message;
        }
    }
}

// index.js
Debug.mode = Debug.Modes.DEV;

如何在 Typescript 中创建这样的匿名对象?使用枚举作为属性?还有一个getter函数?所有属性都是已知的。 我真的被困住了。

【问题讨论】:

  • 此代码已经是有效的打字稿。你还需要它做什么?

标签: javascript typescript


【解决方案1】:

我倾向于解决这些场景的方法是只为匿名对象的属性创建接口,然后为匿名对象创建接口:

enum Modes {
    DEV     = 0,
    PROD    = 1,
}

interface IDebug {
    mode: Modes | null;
    Modes: typeof Modes;
    readonly isInProdMode: boolean;
    assert: (condition: boolean, message: string) => void;
}

const Debug: IDebug = {
    mode: null,
    Modes,
    get isInProdMode() {
        return Debug.mode === Debug.Modes.PROD;
    },
    assert: (condition, message) => {
        if (condition) {
            if (Debug.isInProdMode) {
                console.warn(message);
            } else {
                throw message;
            }
        }
    },
};

export default Debug;

【讨论】:

  • const Debug: IDebug = { Modes, // ... } 我永远不会认为这是可能的。 Ô.Ô
【解决方案2】:

我会说惯用的方法是使用命名空间。

namespace Debug {
  export enum Modes { DEV, PROD }

  export var mode: Modes = Modes.DEV;

  export function isInProdMode(): boolean {
    return mode === Modes.PROD;
  }

  export function assert(condition: boolean, message: string) {
    if (condition) {
      if (isInProdMode()) {
        console.warn(message);
      } else {
        throw message;
      }
    }
  }
} 

export default Debug

但是,命名空间不支持 getter 和 setter,因此需要将 getter 转换为常规函数。

如果需要将此代码声明为对象,则可以先定义枚举,然后从对象中引用。

enum Modes { DEV, PROD }

const Debug = {
  mode: Modes = Modes.DEV,
  get isInProdMode(): boolean {
    return Debug.mode === Modes.PROD;
  },
  assert(condition: boolean, message: string) {
    // ...
  }
}

【讨论】:

  • 请停止使用 TypeScript 命名空间。它们阻止您使用 ES6 模块导入。是时候把那段悲伤的历史抛在脑后了!
  • @LarsGyrupBrinkNielsen 听起来您将命名导出与默认导出混为一谈,并将其归咎于命名空间。默认导出对象与默认导出命名空间有所有相同的问题。
  • 或者,如果您确定,请随意阅读 TypeScript 编译器的源代码,它大量使用命名空间作为一项功能,并告诉他们不要使用它。它们仍然是创建作用域类型的重要构造。
  • 我说的是能够使用import { someStuff } from './some-es6-module';。如果您知道如何在具有命名空间的文件中执行此操作,请告诉我。从 ES6 模块访问命名空间的导出成员也是如此。
  • 这是一个命名的导出,如果你像 OP 一样使用export default,那么对于对象或命名空间是不可能的。
【解决方案3】:
// index.js
import Debug from './debug';

Debug.mode = Debug.Modes.DEV;
console.log(Debug.isInProdMode);
// false

【讨论】:

  • 我的问题是关于转换调试模块。
  • 对不起,我好像没听懂你的意思。 转换是什么意思?
  • 例如,使Modes一个枚举。或者向assert 函数添加签名。我没能做到,因为它在一个匿名对象中,而不是在类声明中。但我从@pete 那里得到了答案。谢谢。
猜你喜欢
  • 2021-09-19
  • 1970-01-01
  • 2016-11-10
  • 2015-06-07
  • 2021-02-28
  • 2019-07-14
  • 1970-01-01
  • 1970-01-01
  • 2020-12-17
相关资源
最近更新 更多