Reputation: 277
So below is my example testing code to implement into the wider solution. I've created an object of "services", each property is the service name with the value being the service object. Each service object holds a property of "commands" with the value being an object of commands (classe's).
These commands are executed via the CLI which always returns a string which is then split. index 0 of the array would be the service name, whilst index 1 would be the name of the command.
// Example Classes
class A {
init() {
console.log('I Am A');
}
}
class B {
init() {
console.log('I Am B')
}
}
// Example Services Structure
export const services = {
service1: {
commands: {
a: A
}
},
service2: {
commands: {
b: B
}
}
}
// Type Declarations
export type Services = typeof services;
export type ServicesKeys = keyof Services;
// Testing
const input = (prompt('Choose a Service') || '').split(' ');
if (input.length !== 2) Deno.exit();
if (input[0] in services) {
const sKey = input[0] as ServicesKeys;
const service = services[sKey];
const commands = service.commands;
if (input[1] in commands) {
// Run into issues here as `keyof typeof commands` is `never`
new commands[input[1] as keyof typeof commands]();
}
}
Everything essentially works fine until the new commands[input[1] as keyof typeof commands]();
as the type of keyof typeof commands
is set to never. Which I understand as commands
can't have a
AND b
so keyof has to be never
but how do I work with this?
Upvotes: 0
Views: 80
Reputation: 33749
You just need to define the type for your services
object, like in the refactor below. If you want to restrict the keys for any part of the structure, you can simply replace string
with your union/enum/etc.
Note: I supplied a substitute for the Deno
namespace APIs used in your code so that you can run the playground example directly in your browser.
const Deno = {
exit (code: number = 0): never {
throw new Error(`Exited with code ${code}`);
}
};
// Example Classes
type Command = {
init (): void;
};
class A implements Command {
init () {
console.log('I Am A');
}
}
class B implements Command {
init () {
console.log('I Am B')
}
}
// Type Declarations
type Service = {
commands: {
[commandName: string]: new () => Command;
};
};
type Services = { [serviceName: string]: Service };
// Example Services Structure
const services: Services = {
service1: {
commands: {
a: A
}
},
service2: {
commands: {
b: B
}
}
}
// Testing
const input = (prompt('Choose a Service') || '').split(' ');
if (input.length !== 2) Deno.exit();
const [serviceName, commandName] = input;
let command: Command | undefined;
if (serviceName in services) {
const {commands} = services[serviceName];
if (commandName in commands) {
command = new commands[commandName]();
}
}
command ? command.init() : console.log('No match');
Upvotes: 1