Generi c Фабрика, использующая Enum, которая заполняет поля - PullRequest
0 голосов
/ 18 апреля 2020

Я не могу понять, как заставить эту фабрику createLetterMap генерировать значения по умолчанию для своего экземпляра ...

«Как l oop over enums», на самом деле, не вопрос, насколько я понимаю это невозможно, потому что типы недоступны во время выполнения, но, может быть, я что-то упускаю из строковых эмуляторов?


enum Letter{
    A = "A",
    B = "B",
    E = "E",
    I = "I",

}
type Vowl = Letter.A | Letter.E | Letter.I

class LMAP{
    test(){}
}
function createLetterMap<T extends Letter>(){
    let lmapExtended = new LMAP() as LMAP & {
        [key in T]?:string
    };

    // ?? create default values for keys somehow ??

    return lmapExtended;
}

let vowlMap = createLetterMap<Vowl>()

vowlMap[Letter.E] = "Eat" // OK
// vowlMap[Letter.B] = "Bat" // Error! Good!

let defaultVal = vowlMap[Letter.A]; // undefined. Would like it to be populated.

В целом, я хотел бы использовать объединения строковых перечислений для получения объекты с ключами, в которых я могу использовать перечисления в качестве ключей для ситуаций вроде этого:

fn(v:Vowl){

  ...

  letterMap[v].someVowlReleatedWork()

  ...

}

Я исследовал альтернативы, просто используя реальную Карту, которая ~ работает ~, но кажется, что есть способ сделать вещи чище, если я могу просто правильно указать типы ...

Лучшее, что я придумал, - это создание дополнительного массива перечислений, включенных в тип объединения, и использование ОБА и объединения, и массива для фабрики. ; что-то вроде ниже, что кажется глупым:

...

let Vowls = [Letter.A,... ]
createLetterMap<Vowl>(Vowls)

Ответы [ 2 ]

0 голосов
/ 19 апреля 2020

Подход "" clean-i sh "", который я обнаружил (по крайней мере, когда дело доходит до использования выходных данных), использует константный массив перечислений. Решение немного ... густое; и я думаю, что существует некоторый серьезный риск перезаписи полей LMAP входной коллекцией перечислений ...

Хотя при назначении значений по умолчанию, кажется, мне нужно привести к любому, но я не уверен почему. Учитывая, что входной массив должен быть набором буквенных перечислений, я не беспокоюсь насчет приведения, скажем, но это немного странно ...

enum Letter{
    A = "A",
    B = "B",
    E = "E",
    I = "I"    
}

// By using a const array for subsets we can have an iterable structure and something to derive type from
const Vowls = <const> [
    Letter.A,
    Letter.E,
    Letter.I
];

// Whatever class we'll 'extend'
class LMAP{};
// Accepts a const array of the original enum type, uses it for type info and iteration 
function createLMAP<T extends Readonly<Array<Letter>>,T2>(letterArray:T,defaultVal:T2){    
    let n = new LMAP() as LMAP & {[key in typeof letterArray[number]]:T2};
    letterArray.forEach(key=>{                
        n[key as T[number]] = defaultVal as any; // Not sure why the need to cast to any here...
    });
    return n;
}

// voila
let vm = createLMAP(Vowls,0);

// tests
Vowls.forEach(v=>{
    vm[v] += 1;
})
vm[Letter.A] = 1;
vm[Letter.B] = 1; // Error thanks to no implicit any

// derive a union type for use elsewhere...
type Vowl = typeof Vowls[number]; 
// e.g.
function work(v:Vowl){
    vm[v] = 1;
}
work(Letter.A)
work(Letter.B) // Err !

Я не принимаю это как ответ, но я иду этим путем лично.

0 голосов
/ 18 апреля 2020

Вы не можете использовать определения типов в качестве значений, поскольку они удаляются во время выполнения - for(let key of keyof Vowl не будет работать.

Вы можете реализовать карту как класс или если вы действительно хотите извлечь ключи во время выполнения Вы можете использовать собственный преобразователь, такой как ts-transformer-keys .


enum Letter {
  A = "A",
  B = "B",
  E = "E",
  I = "I",

}
type Vowl = Letter.A | Letter.E | Letter.I

interface IMapper<T> { operation(value: T): void }

class VowlMapper implements IMapper<Vowl> {
  operation(vowel: Vowl) {
    console.log("VowlMapper: " + vowel);
  }
}

class LetterMap<T extends Letter> {

  private map: IMapper<T> & { [key in T]?: string };

  constructor(map: IMapper<T>) {
    this.map = map;
  }

  get(letter: T) {
    return this.map[letter] ?? letter;
  }

  set(letter: T, value: any) {
    this.map[letter] = value;
  }

  operation(value: T) {
    return this.map.operation(value);
  }

}

let vowlMap = new LetterMap(new VowlMapper())

vowlMap.set(Letter.E, "Eat");
// vowlMap.set(Letter.B, "Bat"); // Error! Good!

console.log(vowlMap.get(Letter.A))  // A
//console.log(vowlMap.get(Letter.B))  // error
console.log(vowlMap.get(Letter.E))  // Eat

vowlMap.operation(Letter.A); // VowlMapper: A 

Ознакомьтесь с примером детской площадки .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...