这感觉就像一个非常简单的问题,但我无法理解它或找到正确的例子。
让我们说我想创建一个ServiceProvider
类,它在内部将类实例映射到字符串标识符或实例构造函数。使用时它看起来像这样:
let provider = new ServiceProvider();
provider.set('date', new Date());
provider.set('other', new SomeObject());
provider.get('date'); // I want TypeScript to know this is a date object
provider.get('other'); // I want TypeScript to know this is an instance of SomeObject
或者像这样:
let provider = new ServiceProvider();
provider.set(new Date());
provider.set(new SomeObject());
provider.get(Date); // I want TypeScript to type-safely return the Date I provided
provider.get(SomeObject); // I want TypeScript to type-safely return the instance of SomeObject I provided
该解决方案与this article中给出的解释非常相似,但在示例中,存储对象与密钥一起提供,而不是存储在其他位置。我探讨了以下示例,但T extends typeof this.data
无法解析。
export class ServiceProvider
{
private data: {};
public set(key: string, instance: {}): void
{
this.data[key] = instance;
}
public get<T extends typeof this.data, K extends keyof T>(key: K): T
{
return this.data[key];
}
}
我不确定将data
声明为什么类型;我不能强烈地键入它(private data: { [key: string]: Date }
),因为我正在处理未知的实例类型,但如果我松散地键入它(private data: { [key: string]: object }
),那么我可以分配任何对象,但我认为类型信息正在丢失。
任何想法都会有所帮助。
答案 0 :(得分:2)
您的第二种方法(传递构造函数以获取该构造函数的唯一实例)很简单:
public get<T>({new(): T}): T
答案 1 :(得分:1)
我将重点关注string
- 值键的部分,并忽略传递构造函数问题(@SLaks或多或少地正确键入)。
如果您事先知道哪些键与哪种类型相关,那么您应该只使用普通对象:
interface ServiceProvider {
date: Date,
other: SomeObject
}
如果您事先不知道但希望能够动态添加密钥,那么没有静态类型的对象可以为您工作。你可以做的是让setter返回一个新类型的对象。然后,TypeScript会跟踪哪些键与哪些值类型一致,但您必须链接您的setter。这是一个示例实现:
interface EmptyServiceProvider {
set<K extends string, V>(k: K, v: V): ServiceProvider<K, V>;
}
interface ServiceProvider<K extends string, V> extends EmptyServiceProvider {
set<K extends string, V>(k: K, v: V): this & ServiceProvider<K, V>;
get(k: K): V;
}
function emptyProvider(): EmptyServiceProvider {
const data = {} as { [k: string]: any };
const provider = {
get(k: string) {
return data[k];
},
set(k: string, v: any) {
data[k] = v;
return provider;
}
};
return provider;
}
观看它的实际操作:
let provider = emptyProvider()
.set('date', new Date()) //chaining
.set('other', new SomeObject()); //chaining
const d = provider.get('date'); // inferred as Date
const o = provider.get('other'); // inferred as SomeObject
const x = provider.get('whoops'); // error, "whoops" doesn't work
这样可行,但提供者的监管链很重要。如果有人向您提供了来自未知来源的ServiceProvider
,您可能无法让TypeScript了解从键到值类型的映射:
declare const providerFromSomewhereElse: ServiceProvider<string, any>
const hmm = providerFromSomewhere.get('date'); // any
所以我不确定这对你有多大用处,但是我能在TypeScript中找到最接近动态类型对象的东西。希望有所帮助;祝好运!