使用 AMD 时如何在 d.ts 文件中引用 Typescript 枚举?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38335668/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to refer to Typescript enum in d.ts file, when using AMD?
提问by Stephan G
I want to define a typescript interface to represent, say, an error. Something like this:
我想定义一个打字稿接口来表示,比如说,一个错误。像这样的东西:
enum MessageLevel {
Unknown,
Fatal,
Critical,
Error,
Warning,
Info,
Debug
}
interface IMyMessage {
name: string;
level: MessageLevel;
message: string;
}
This works fine as far as it goes. However, now (perhaps) I want to declare that interface in a .d.ts file so others can use it for typing. But I don't want to define the enum in the .d.ts file, since that would be implementation and not simple typing information. The enum should presumably be in a .ts file, let's call it messageLevel.ts:
就目前而言,这工作正常。但是,现在(也许)我想在 .d.ts 文件中声明该接口,以便其他人可以使用它进行输入。但我不想在 .d.ts 文件中定义枚举,因为那将是实现而不是简单的输入信息。枚举应该在一个 .ts 文件中,我们称之为 messageLevel.ts:
///<amd-module name='MessageLevel'/>
export enum MessageLevel {
Unknown,
Fatal,
Critical,
Error,
Warning,
Info,
Debug
}
and I can, at this point, use it in my d.ts typing file this way:
在这一点上,我可以这样在我的 d.ts 输入文件中使用它:
import * as ml from "./MessageLevel";
interface IMyMessage {
name: string;
level: ml.MessageLevel;
message: string;
}
and I can make this work, but I don't like the level-mixing of importing an implementation file into a typing file. Nor do I like the idea of actually implementing an enum in a typings file.
我可以完成这项工作,但我不喜欢将实现文件导入到类型文件中的级别混合。我也不喜欢在类型文件中实际实现枚举的想法。
Is there a clean way to do this that keeps implementation and declaration strictly separate?
是否有一种干净的方法可以将实现和声明严格分开?
采纳答案by Katana314
The best solution may depend on whether you have a preference for the actual JavaScript variable being a number, a string, or otherwise. If you don't mind String, you can do it like this:
最佳解决方案可能取决于您是否更喜欢实际的 JavaScript 变量是数字、字符串或其他。如果你不介意字符串,你可以这样做:
///messagelevel.d.ts
export type MessageLevel = "Unknown" | "Fatal" | "Critical" | "Error";
///main.d.ts
import * as ml from "./MessageLevel";
interface IMyMessage {
name: string;
level: ml.MessageLevel;
message: string;
}
So in the end JavaScript, it will simply be represented as a string, but TypeScript will flag an error anytime you compare it to a value not in that list, or try to assign it to a different string. Since this is the closest that JavaScript itself has to any kind of enum (eg, document.createElement("video")
rather than document.createElement(ElementTypes.VIDEO)
, it might be one of the better ways of expressing this logic.
因此,最终 JavaScript 将简单地表示为一个字符串,但只要您将它与不在该列表中的值进行比较,或者尝试将其分配给不同的字符串,TypeScript 就会标记错误。由于这是 JavaScript 本身最接近任何类型的枚举(例如,document.createElement("video")
而不是document.createElement(ElementTypes.VIDEO)
,它可能是表达此逻辑的更好方法之一。
回答by Flávio Lisb?a
I was thinking about this issue these last couple of days, and perhaps a const enum
, coupled with union types, may be a suitable option.
这几天我一直在考虑这个问题,也许 aconst enum
加上联合类型可能是一个合适的选择。
This approach depends on the fact that your API clients can expect some enum that is not explicitly declaredin your API files.
这种方法取决于这样一个事实,即您的 API 客户端可以期待一些未在您的 API 文件中明确声明的枚举。
Consider this. First, the API file api.d.ts
:
考虑一下。首先,API文件api.d.ts
:
/**
* @file api.d.ts
*
* Here you define your public interface, to be
* implemented by one or more modules.
*/
/**
* An example enum.
*
* The enum here is `const` so that any reference to its
* elements are inlined, thereby guaranteeing that none of
* its members are computed, and that no corresponding
* JavaScript code is emmitted by the compiler for this
* type definition file.
*
* Note how this enum is named distinctly from its
* "conceptual" implementation, `MyEnum`.
* TypeScript only allows namespace merging for enums
* in the case where all namespaces are declared in the
* same file. Because of that, we cannot augment an enum's
* namespace across different source files (including
* `.d.ts` files).
*/
export const enum IMyEnum { A }
/**
* An example interface.
*/
export interface MyInterface {
/**
* An example method.
*
* The method itself receives `IMyEnum` only. Unfortunately,
* there's no way I'm aware of that would allow a forward
* declaration of `MyEnum`, like one would do in e.g. C++
* (e.g. declaration vs definition, ODR).
*/
myMethod(option: IMyEnum): void;
}
And an API implementation, impl.ts
:
和一个 API 实现,impl.ts
:
/**
* @file impl.ts
*/
/**
* A runtime "conceptual" implementation for `IMyEnum`.
*/
enum MyEnum {
// We need to redeclare every member of `IMyEnum`
// in `MyEnum`, so that the values for each equally named
// element in both enums are the same.
// TypeScript will emit something that is accessible at
// runtime, for example:
//
// MyEnum[MyEnum["A"] = 100] = "A";
//
A = IMyEnum.A
}
class MyObject implements IMyInterface {
// Notice how this union-typed argument still matches its
// counterpart in `IMyInterface.myMethod`.
myMethod(option: MyEnum | IMyEnum): void {
console.log("You selected: " + MyEnum[option]);
}
}
// ----
var o = new MyObject();
o.myMethod(MyEnum.A); // ==> You selected: 100
o.myMethod(IMyEnum.A); // ==> You selected: 100
// YAY! (But all this work shouldn't really be necessary, if TypeScript
// was a bit more reasonable regarding enums and type declaration files...)
I made this gist as an example, in case someone would like to see this approach in action.
我将此要点作为示例,以防有人希望看到这种方法的实际应用。
回答by Damian Dobrev
Almost two years later, this problem still exists. I could not find a good solution so I created a workaround, which tells your interface only that the type of the var is an enum, but not which enum. There's a "middleware" abstract wrapper for your main class which concretely sets the var type to be the needed enum.
差不多两年过去了,这个问题仍然存在。我找不到一个好的解决方案,所以我创建了一个解决方法,它只告诉你的界面 var 的类型是一个枚举,而不是哪个枚举。主类有一个“中间件”抽象包装器,它具体地将 var 类型设置为所需的枚举。
// globals.d.ts
type EnumType = { [s: any]: any }
interface IMyMessage {
level: EnumType
}
// enums.ts
export enum MessageLevel {
Unknown,
Fatal,
Critical,
Error,
Warning,
Info,
Debug
}
// MyClass.ts
import { MessageLevel } from 'enums'
// If your MyMessage class is extending something, MyMessageWrapper has to
// extend it instead!
abstract class MyMessageWrapper extends X implements IMyMessage {
abstract level: MessageLevel
}
class MyMessage extends MyMessageWrapper {
level = MessageLevel.Unknown // works
// level = MyOtherEnum.Unknown // doesn't work
}
Might be useful in some use cases.
在某些用例中可能有用。