Note: following code is a simplified version to focus only on the issue. Intro is a bit long, but needed for clarity.
I have a Foo class that represent a complex object.
interface Config {
bars:{
[key:string]: {
on?: {
[key:string]: (m:any) => void
}
}
}
}
class Foo<T extends Config> {
public constructor(private config:T) {}
public doSomething(eventName: keyof T["bars"]) {}
}
Its configuration comes from an object passed in the constructor. So with a initialization like this :
const foo = new Foo({
bars: {
buz1: { },
buz2: { }
}
})
foo.doSomething("buz1");
foo.doSomething("foo");
The first doSomething is fine, the second raise an error which is expected and desired. My problem is in the nested buz* which needs to expose an on property that will have eventNames and associated callback called when the event is raised :
const foo = new Foo({
bars: {
buz1: {
on: {
"event": (f:Foo<THereIsTheIssue>) => {
f.doSomething("buz2")
}
}
},
buz2: { }
}
})
I want f to be of the same type as foo, but I can't find a way to tell that to Typescript. Closest I came to a working solution so far is with this :
interface Config<U extends Config<U>> {
bars:{
[key:string]: {
on?: {
[key:string]: (m:Foo<U>) => void
}
}
}
}
class Foo<T extends Config<T>> {
public constructor(private config:T) {}
public doSomething(eventName: keyof T["bars"]) {}
}
function tmp() {
const foo = new Foo({
bars: {
buz1: {
on: {
"event": (f) => {
f.doSomething("")
}
}
},
buz2: { }
}
});
foo.doSomething("buz1");
foo.doSomething("foo");
}
Only issue f is of type Foo<Config<unknown>> which prevents it from being assigned to event.
So how could I make Typescript know that type from the what is passed to the constructor (if it's even possible) ?
The constraints :
- types can be split up or in a single type/interface (but there are many other properties)
barsandonare fixed keywords which needs to be nested as they are herebuz*are not known and will be specific to the developer/project
Gist and TS playground for the code where I'm at so far.