gpt4 book ai didi

TypeScript:在具有类型安全性的运行时向类添加动态字段

转载 作者:行者123 更新时间:2023-12-04 13:05:33 26 4
gpt4 key购买 nike

我正在将一些遗留 JS 代码转换为 TS,并且遇到了一个有趣的输入情况。有一个模块系统在运行时将类实例加载到另一个类实例的命名属性上,并试图弄清楚如何键入对象:

class Module {
public name: string;
}

class ThisModule extends Module {
public name = 'thisModule';

public log = () => {
console.log('ThisModule called!');
};
}

class ThatModule extends Module {
public name = 'thatModule';

public log = () => {
console.log('ThatModule called!');
};
}

interface IModule extends Module {}

type ModulesInput = Module[];

class Container {
public init = (modules: ModulesInput) => {
for (const module of modules) {
this[module.name] = module;
}
};
}

const thisModule = new ThisModule();
const thatModule = new ThatModule();

const container = new Container<ThisModule | ThatModule >();

const modules: ModulesInput = [
thisModule,
thatModule,
];
container.init(modules);

// Should not throw TS error
container.thisModule.log();
container.thatModule.log();

// Should throw error ("Property anythingElse does not exist on Container")
container.anythingElse.log();

问题在于 TypeScript 无法识别 container.thisModulecontainer.thatModule 应该存在。它说 TS2339: Property 'thisModule' does not exist on type 'Container'。(对于 thatModule,也是如此)。

有没有办法输入这个系统?到目前为止,我在 Container 类中添加多个泛型取得了一些有限的成功(例如 type keys = 'thisModule' | 'thatModule'type ModulesInUse = ThisModule | ThatModule),但是 TypeScript 能否从类中发现名称并动态学习它应该期望 container 对象上的那些键具有其各自类的类型?

提前致谢!

最佳答案

为了消除这一行的错误 this[module.name] = module;您需要提供索引签名:[prop: string]: Module .

请注意,除了一个 case 之外,typescript 不会跟踪突变,因此您需要返回新的 this来自 init属性(property)。

完整代码:

class Module {
public name: string = '' // need to be initialized
}

class ThisModule extends Module {
public name = 'thisModule' as const;

public log = () => {
console.log('ThisModule called!');
};
}

class ThatModule extends Module {
public name = 'thatModule' as const;

public log = () => {
console.log('ThatModule called!');
};
}


type Reduce<
T extends ReadonlyArray<Module>,
Acc extends Record<string, unknown> = {}
> =
(T extends []
? Acc
: (T extends [infer Head, ...infer Tail]
? (Tail extends Module[]
? (Head extends Module
? Reduce<Tail, Acc & Record<Head['name'], Head>>
: never)
: never)
: never)
)

class Container {
[prop: string]: Module; // you need to provide index signature to class in order to use this[module.name] = module

public init = <M extends Module, Modules extends M[]>(modules: [...Modules]) => {
for (const module of modules) {
this[module.name] = module;
}

return this as Reduce<[...Modules]>
};
}

const thisModule = new ThisModule();
const thatModule = new ThatModule()

const container = new Container();


const newContainer = container.init([
thisModule,
thatModule,
]);


// Should not throw TS error
newContainer.thisModule.log()
newContainer.thatModule.log();

// Should throw error ("Property anythingElse does not exist on Container")
newContainer.anythingElse.log() // error

Playground

我用过Reduce实用程序类型,它的工作方式几乎类似于 Array.prototype.reduce , 它将元组中的所有元素折叠到一个对象中。

另外,我用过variadic-tuple-typesinit 推断每个模块争论 <M extends Module, Modules extends M[]>(modules: [...Modules]) .

Here is also a version that uses asserts this is ... to perform the assertion in place instead of returning the new value.

关于TypeScript:在具有类型安全性的运行时向类添加动态字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69605677/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com