I'm trying to type this class which was initially written in JavaScript.
Here is the code snippet.
type SchemaDefinition<Schema> = {[Key in keyof Schema]: Schema[Key][] | {[K in keyof Schema[Key]]: SchemaDefinition<Schema[Key][K]>}};
type Middlewares<Schema> = {[Key in keyof Schema]: (value: Schema[Key]) => Schema[Key] | {[K in keyof Schema[Key]]: Middlewares<Schema[Key][K]>}};
class ObjectGenerator<Schema> {
private schemaDefinition: SchemaDefinition<Schema>;
constructor(schemaDefinition: SchemaDefinition<Schema> = {} as SchemaDefinition<Schema>) {
this.schemaDefinition = schemaDefinition;
}
generate(middlewares: Middlewares<Schema> = {} as Middlewares<Schema>): Schema {
const {schemaDefinition} = this;
return Object.entries(schemaDefinition).reduce((schema, [property, values]) => {
if (Array.isArray(values)) {
return {...schema, [property]: (middlewares[property] || ((x: any): any => x))(values[Math.floor(Math.random() * values.length)])};
} else if (typeof values === "object") {
return {...schema, [property]: new ObjectGenerator(values).generate(middlewares[property])};
}
return schema;
}, {} as Schema);
}
}
And the error I got is.
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Middlewares<Schema>'. No index signature with a parameter of type 'string' was found on type 'Middlewares<Schema>'
What I expect from this is for TypeScript to not only guess the type of the things I pass it at compile time (like for the Schema that gets turned into a SchemaDefinition) but also to support recursive calls and still be able to guess the type of the nested properties in the return output so that clients can be warned when they try to access either a nested property that does not exist, calling a middleware on a property that does not exist or calling a middleware with the wrong type.
Object.entries()is the issue, as the compiler cannot be sure that it knows all the properties of an object; see this answer for more info. You can do an assertion likeObject.entries(schemaDefinition) as Array<[keyof Schema, Schema[keyof Schema]]>if you want to sidestep it (no worse than{} as SchemaDefinition<Schema>, which seems more suspicious to me). If this is your main problem I will probably close the issue as a duplicate of others referencingObject.entries(); otherwise please elaborate. Good luck! - jcalz