首页 > 解决方案 > 无法访问对象的深层属性/值

问题描述

所以下面是我的示例测试代码,以实现更广泛的解决方案。我创建了一个“服务”对象,每个属性都是服务名称,值是服务对象。每个服务对象都拥有一个“命令”属性,其值是命令对象(类)。

这些命令通过 CLI 执行,该 CLI 始终返回一个字符串,然后将其拆分。数组的索引 0 将是服务名称,而索引 1 将是命令的名称。

// 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]();
    }
}

new commands[input[1] as keyof typeof commands]();在as 的类型keyof typeof commands设置为 never之前,一切基本上都可以正常工作。我理解commands不能拥有ab所以 keyof 必须是,never但我该如何处理呢?

标签: javascripttypescripttypesdeno

解决方案


您只需要为您的services对象定义类型,就像在下面的重构中一样。如果你想限制结构任何部分的键,你可以简单地string用你的 union/enum/etc 替换。

注意:我提供了代码中使用的命名空间 API 的替代品,Deno以便您可以直接在浏览器中运行 Playground 示例。

TS Playground 链接

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');

推荐阅读