typescript - Extinderea unui enum importat într-un alt fișier și utilizarea acestuia
Încerc să extind un enum importat într-un fișier diferit și să folosesc acel enum extins într-un alt fișier diferit.
Cazul general
bază.enum.ts
export enum MyEnum {
a = "Foo"
}
extins.enum.ts
import { MyEnum } from './base.enum';
declare module './base.enum' {
export enum MyEnum {
b = "Bar"
}
}
Utilizare în index.ts
import { MyEnum } from './base.enum';
import './extended.enum'; // side-effects import (no usage of export)
console.log(MyEnum.a); // prints "Foo" as expected
console.log(MyEnum.b); // prints undefined, instead of the expected "Bar"
(Fac asta în TypeScript 2.4.2, care suportă enum-uri cu valori de tip string)
Am folosit această și aceste întrebări de pe SO ca referință și am citit următoarea problemă TypeScript de pe GitHub și tot nu am reușit să găsesc o soluție pentru problema mea.
Exemplu de utilizare care seamănă cu al meu
Enum de bază AnimalTypes în animals/base/animal-types.enum.ts:
export enum AnimalTypes { }
Interfața de bază Animal în animals/base/animal.ts:
import { AnimalTypes } from './animal-types';
export interface Animal {
type: AnimalTypes;
}
animals/base/index.ts:
export * from './animal-types.enum';
export * from './animal';
Enum extins AnimalTypes în animals/animal-types.enum.ts:
import { AnimalTypes } from './base/';
declare module './base/animal-types.enum' {
export enum AnimalTypes {
Cat = "cat",
Dog = "dog"/*,
...*/
}
}
Clasa concretă Cat în animals/cat.ts:
import { Animal, AnimalTypes } from './base/';
import './animal-types.enum';
export class Cat implements Animal {
public type: AnimalTypes = AnimalTypes.Cat; // Usage of AnimalTypes with extended value
}
Clasa concretă Dog în animals/dog.ts:
import { Animal, AnimalTypes } from './base/';
import './animal-types.enum';
export class Dog implements Animal {
public type: AnimalTypes = AnimalTypes.Dog; // Usage of AnimalTypes with extended value
}
animale/index.ts:
export * from './cat';
export * from './dog';
//...
Utilizare finală în animale/animals-manager.ts:
import { Animal, AnimalTypes} from './base/';
import { Cat, Dog/*, ...*/ } from '.';
import './animal-types'; // side-effects import (no usage of export)
export class AnimalsManager {
public animals: { [animal: string]: Animal } = {}; // Animal dictionary (I would use AnimalTypes as key type but that isn't supported yet as far as I know).
constructor() {
this.animals[AnimalTypes.Cat] = new Cat();
this.animals[AnimalTypes.Dog] = new Dog();
}
//...
}
Când încerc să folosesc animals[AnimalTypes.Cat] din AnimalManager, voi obține valoarea lui animals[AnimalTypes.Dog] deoarece atât AnimalTypes.Cat, cât și AnimalTypes.Dog returnează undefined (ceea ce înseamnă că animals[AnimalTypes.Cat] a fost suprascris prin setarea lui animals[AnimalTypes.Dog]).
Așadar, există în prezent o modalitate de a extinde enum-urile importate în TypeScript așa cum este descris mai sus sau va trebui să găsesc o soluție neoficială pentru a obține un enum personalizat care să suporte o astfel de extindere? Mulțumesc.
Răspuns #1
TypeScript permite extinderea doar a informațiilor despre tip în declare module, niciun cod nu este niciodată executat din aceste declarații. De fapt, a avea cod în interiorul declare module este în general interzis, de exemplu plasarea unei funcții cu un corp acolo va genera eroarea An implementation cannot be declared in ambient contexts.
Deci ai declarat un membru suplimentar pentru tipul de enumerare MyEnum, dar acel membru nu este inițializat nicăieri, așa că valoarea sa este undefined la rulare. Pentru a remedia asta, îl poți inițializa tu însuți:
extins.enum.ts
import { MyEnum } from './base.enum';
declare module './base.enum' {
export enum MyEnum {
b = "Bar"
}
}
const myEnumValues = MyEnum as any;
myEnumValues["b"] = "Bar";
Trebuie să convertești MyEnum mai întâi în any, deoarece inițializarea directă nu este permisă:
MyEnum["b"] = "Bar";
nu se compilează din cauza unei erori
error TS2540:Cannot assign to 'b' because it is a constant or a read-only property.
Răspunsul #2
Am dat peste această postare veche chiar acum când încercam să înțeleg cum un proiect de la muncă reușea să facă asta, dar nu am reușit să fac același lucru în propriul meu proiect (la fel ca problema OP).
Nu sunt sigur dacă acest lucru este acum acceptat în versiunea mai nouă de TypeScript (acum folosesc 4.7), dar acest lucru este posibil folosind enumuri const.
Urmând același exemplu:
bază-enum.ts
export const enum MyEnum {
a = "Foo"
}
extended-enum.ts
declare module "./base-enum" {
const enum MyEnum {
b = "Bar"
}
}
export {}; // make this a module
main.ts
import { MyEnum } from "./base-enum";
console.log(MyEnum.a);
console.log(MyEnum.b);
Executarea acestuia afișează corect:
Foo
Bar
main.js compilat arată astfel (module este setat la "commonjs" în tsconfig.json), cu compilatorul înlocuind referința la enum cu constanta.
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
console.log("Foo" /* MyEnum.a */);
console.log("Bar" /* MyEnum.b */);
//# sourceMappingURL=main.js.map
A remarca diferențele față de exemplul OP-ului în mod specific:
- Folosește un
const enum - În declarația de extindere, nu face
export - Asigură-te că fișierul cu declarația de extindere este un modul (adică trebuie să aibă o instrucțiune import/export)
