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.