gpt4 book ai didi

typescript - 在 TypeScript 中实现类型安全的服务注册表

转载 作者:行者123 更新时间:2023-12-03 22:57:54 25 4
gpt4 key购买 nike

我想要一个基于给定标识符(例如字符串或符号)返回对象实例的函数。在代码中,它可能看起来像:

// define your services
type ServiceA = { foo: () => string };
const ServiceA = { foo: () => 'bar' };

type ServiceB = { bar: () => number };
const ServiceB = { bar: () => 1 };

// register your services with identifier
registry.register('serviceA', ServiceA);
registry.register('serviceB', ServiceB);

// get a service by its type identifier
// the important thing here: I want to know the type!
const serviceA: ServiceA = registry.get('serviceA');
serviceA.foo();

进一步要求: ServiceAServiceB不要共享一个界面左右,它们可以完全不同。

所示示例中的挑战是要知道 registry.get 返回的值的确切类型。 .

我尝试了一种天真的方法,例如:
enum ServiceType {
A,
B
}

const getService = (type: ServiceType): ServiceA | ServiceB => {
switch (type) {
case ServiceType.A:
return ServiceA;
case ServiceType.B:
return ServiceB;
default:
throw new TypeError('Invalid type');
}
};

if 也是一样的但是当我执行 const x = getService(ServiceType.A); 时,编译器无法导出返回值的具体类型x 的类型是 ServiceA | ServiceB ,我想看的是 ServiceA .

有没有办法做这样的事情?如果不是,从编译器的角度来看,这是什么原因?

最佳答案

如果您事先知道将从注册表返回的服务类型,则可以使用表示从字符串键到服务类型的映射的类型:

type ServiceMapping = {
// first ServiceA is a string key, second is a type
ServiceA: ServiceA;
// first ServiceB is a string key, second is a type
ServiceB: ServiceB;
}

function getService<T extends keyof ServiceMapping>(type: T): ServiceMapping[T] {
return ({
ServiceA: ServiceA,
ServiceB: ServiceB
})[type];
}

// use it
const serviceA = getService('ServiceA');
serviceA.foo(); // works

如果您想要一个注册表,您可以在编译时不断添加内容并跟踪类型,您将需要使用注册表对象链。您不能真正使用常量注册表对象,因为 TypeScript 不会让您根据注册的内容改变对象的类型。但是你可以通过让注册表在调用它的 register() 时返回一个新类型的对象来帮助 TypeScript。方法,并且您只保留返回的对象。像这样:
class ServiceRegistry<T> {  
private constructor(private registry: T) {}
register<K extends string, S>(key: K, service: S): ServiceRegistry<Record<K, S> & T> {
// add service to registry and return the same object with a narrowed type
(this.registry as any)[key] = service;
return this as any as ServiceRegistry<Record<K, S> & T>;
}
get<K extends keyof T>(key: K): T[K] {
if (!(key in this.registry)) {
throw new Error('Invalid type' + key);
}
return this.registry[key];
}
static init(): ServiceRegistry<{}> {
return new ServiceRegistry({});
}
}
所以这里是你如何使用它:
// register things
const registry = ServiceRegistry.init()
.register('ServiceA', ServiceA)
.register('ServiceB', ServiceB);
注意 registry 的类型是最后 register()的返回值的类型方法,它具有从类型键到服务对象的所有相关映射。
const serviceA = registry.get('ServiceA');
serviceA.foo(); // works

Playground link to code

关于typescript - 在 TypeScript 中实现类型安全的服务注册表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47098643/

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