使用 AMD 时如何在 d.ts 文件中引用 Typescript 枚举?
How to refer to Typescript enum in d.ts file, when using AMD?
我想定义一个 typescript 接口来表示错误。像这样:
enum MessageLevel {
Unknown,
Fatal,
Critical,
Error,
Warning,
Info,
Debug
}
interface IMyMessage {
name: string;
level: MessageLevel;
message: string;
}
就目前而言,它工作正常。但是,现在(也许)我想在 .d.ts 文件中声明该接口,以便其他人可以使用它进行输入。但我不想在 .d.ts 文件中定义枚举,因为那将是实现而不是简单的键入信息。枚举应该在 .ts 文件中,我们称它为 messageLevel.ts:
///<amd-module name='MessageLevel'/>
export enum MessageLevel {
Unknown,
Fatal,
Critical,
Error,
Warning,
Info,
Debug
}
此时,我可以在我的 d.ts 键入文件中使用它:
import * as ml from "./MessageLevel";
interface IMyMessage {
name: string;
level: ml.MessageLevel;
message: string;
}
我可以完成这项工作,但我不喜欢将实现文件导入类型文件的层次混合。我也不喜欢在类型文件中实际实现枚举的想法。
有没有一种干净的方法可以使实现和声明严格分开?
最佳解决方案可能取决于您是否偏好实际 JavaScript 变量是数字、字符串还是其他形式。如果你不介意String,你可以这样做:
///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;
}
所以最后 JavaScript,它将简单地表示为一个字符串,但是只要您将它与不在该列表中的值进行比较,或者尝试将其分配给不同的值,TypeScript 就会标记错误细绳。因为这是 JavaScript 本身最接近任何类型的枚举(例如,document.createElement("video")
而不是 document.createElement(ElementTypes.VIDEO)
,它可能是表达这种逻辑的更好方式之一。
这几天我一直在考虑这个问题,也许 const enum
加上联合类型可能是一个合适的选择。
这种方法取决于这样一个事实,即您的 API 客户可以期待一些 未在您的 API 文件中明确声明 的枚举。
考虑一下。首先,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;
}
还有一个 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,以防有人希望看到这种方法的实际应用。
差不多两年过去了,这个问题依然存在。
我找不到好的解决方案,所以我创建了一个解决方法,它只告诉您的界面 var 的类型是一个枚举,而不是哪个枚举。您的主要 class 有一个 "middleware" 抽象包装器,它具体地将 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
}
在某些用例中可能会有用。
我想定义一个 typescript 接口来表示错误。像这样:
enum MessageLevel {
Unknown,
Fatal,
Critical,
Error,
Warning,
Info,
Debug
}
interface IMyMessage {
name: string;
level: MessageLevel;
message: string;
}
就目前而言,它工作正常。但是,现在(也许)我想在 .d.ts 文件中声明该接口,以便其他人可以使用它进行输入。但我不想在 .d.ts 文件中定义枚举,因为那将是实现而不是简单的键入信息。枚举应该在 .ts 文件中,我们称它为 messageLevel.ts:
///<amd-module name='MessageLevel'/>
export enum MessageLevel {
Unknown,
Fatal,
Critical,
Error,
Warning,
Info,
Debug
}
此时,我可以在我的 d.ts 键入文件中使用它:
import * as ml from "./MessageLevel";
interface IMyMessage {
name: string;
level: ml.MessageLevel;
message: string;
}
我可以完成这项工作,但我不喜欢将实现文件导入类型文件的层次混合。我也不喜欢在类型文件中实际实现枚举的想法。
有没有一种干净的方法可以使实现和声明严格分开?
最佳解决方案可能取决于您是否偏好实际 JavaScript 变量是数字、字符串还是其他形式。如果你不介意String,你可以这样做:
///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;
}
所以最后 JavaScript,它将简单地表示为一个字符串,但是只要您将它与不在该列表中的值进行比较,或者尝试将其分配给不同的值,TypeScript 就会标记错误细绳。因为这是 JavaScript 本身最接近任何类型的枚举(例如,document.createElement("video")
而不是 document.createElement(ElementTypes.VIDEO)
,它可能是表达这种逻辑的更好方式之一。
这几天我一直在考虑这个问题,也许 const enum
加上联合类型可能是一个合适的选择。
这种方法取决于这样一个事实,即您的 API 客户可以期待一些 未在您的 API 文件中明确声明 的枚举。
考虑一下。首先,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;
}
还有一个 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,以防有人希望看到这种方法的实际应用。
差不多两年过去了,这个问题依然存在。 我找不到好的解决方案,所以我创建了一个解决方法,它只告诉您的界面 var 的类型是一个枚举,而不是哪个枚举。您的主要 class 有一个 "middleware" 抽象包装器,它具体地将 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
}
在某些用例中可能会有用。