使用 Enum 填充字段的通用工厂
Posted
技术标签:
【中文标题】使用 Enum 填充字段的通用工厂【英文标题】:Generic Factory using Enum that populates fields 【发布时间】:2020-08-01 04:21:13 【问题描述】:我不知道如何让这个工厂 createLetterMap
为其实例生成默认值...
' How to loop over enums '并不是真正的问题,因为我理解这是不可能的,因为类型在运行时不可用,但也许我错过了关于字符串 emums 的一些东西?
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)
【问题讨论】:
【参考方案1】:您不能将类型定义用作值,因为它们会在运行时被删除 - 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
查看playground example。
【讨论】:
是的,仅仅隐藏实际的地图可能是更好的,或者至少更安全的方式......虽然我无法找到一种方式来说明“实例A 有键 K [B,C,D]" 并且可以关闭并写入 A[B] 而无需创建多个间接和组合级别......不过感谢您的方法!【参考方案2】:我发现的“干净”方法(至少在使用输出时)使用 const 枚举数组。解决方案有点……厚;而且我认为用输入枚举集合覆盖 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 !
我不接受这个作为答案,但我个人会走这条路。
【讨论】:
以上是关于使用 Enum 填充字段的通用工厂的主要内容,如果未能解决你的问题,请参考以下文章