繁体   English   中英

Typescript - 如何从变量对象访问变量对象属性

[英]Typescript - How to access a variable object property from a variable object

我在尝试编写一个接受两个输入的函数时遇到了一些困难:

  1. 对象名称
  2. 物业名称

并打印该对象的该属性的值。 然而,这些对象都有不同的属性,它们并不完全相同。

对象如下所示:

class object1 {
  get property1() {
    return 'foo';
  }
  get property2() {
    return 'bar';
  }
}

export default new object1();
class object2 {
  get different1() {
    return 'asdf';
  }
  get different2() {
    return 'ghjk';
  }
}

export default new object2();

到目前为止,这是我尝试过的:

import object1 from '..';
import object2 from '..';

getPropertValue(objectName, propertyName) {
  let objects = [object1, object2];
  let index = objects.indexOf(objectName);
  console.log(objects[index][propertyName]);
}

这没有用,返回为未定义。 似乎 index 计算正确,但objects[index][propertyName]似乎没有正确访问对象的值。 虽然很奇怪,但当我尝试以下操作时,它几乎起作用了:

import object1 from '..';
import object2 from '..';

getPropertValue(objectName, propertyName) {
  let objects = [object1, object2];
  for (let index in objects) {
    console.log(objects[index][propertyName]);
  }
}

在这里我实际上让它打印了正确的值,但问题是因为我只是在 for 循环中遍历所有对象,它会尝试打印所有对象的值,而不仅仅是与 objectName 匹配的对象. 所以它为具有我试图访问的属性的对象打印正确的值,但是对于没有该属性的另一个对象则为未定义。

我想我可以为每个名为name的对象添加一些属性并执行如下操作:

getPropertValue(objectName, propertyName) {
  let objects = [object1, object2];
  for (let index in objects) {
    if(objects[index].name == objectName) {
      console.log(objects[index][propertyName]);
    }
  }
}

但如果可以的话,我宁愿不向这些对象添加不必要的属性。

有一个更好的方法吗?

对象可以被变量引用,但是对象没有被命名的特性,除非您的代码明确地将其作为数据模型的一部分提供。

例如:

const obj = { abc: 123 }

这里的值{ abc: 123 }没有命名为obj 有一个标识符为obj的变量引用了{ abc: 123 }的值。

这在实践中意味着,如果您只有对{ abc: 123 }的引用,那么您无法知道还有哪些其他变量标识符也持有对该值的引用。

而且您不能通过标识符作为字符串来获取变量的值。

所以这不可能工作:

const foo = 'bar'

function getVar(varName: string) {
  // impossible function implementation here.
}

getVar('foo') // expected return value: 'bar'

但是您可以拥有的是具有属性的对象。 属性具有特定字符串的键。

const objects = { // note the {}, [] is for arrays
  object1,
  object2,
}

这是以下内容的简写:

const objects = {
  "object1": object1,
  "object2": object2,
}

您现在拥有的对象的属性与您要用于对象的名称相对应。


最后,让我们正确输入您的函数:

// store this outside the function
const objects = {
  object1,
  object2,
}

function getPropertyValue<
  T extends keyof typeof objects
>(
  objectName: T,
  propertyName: keyof typeof objects[T]
) {
  console.log(objects[objectName][propertyName]);
}

这个函数现在是通用的,这意味着它接受一个类型。 在这种情况下,它接受您要用作T的对象名称,它是objects对象的任何keyof

然后propertyName参数使用任何键来找出应该允许的属性,方法是将类型限制为这些对象之一的属性。

现在要获取数据,您可以通过objects上的属性名称钻取objects ,然后再次钻取特定属性名称。

用法如下所示:

getPropertyValue('object1', 'property1') // 'foo'
getPropertyValue('object2', 'different1') // 'asdf'

getPropertyValue('object1', 'different1')
// Type error, 'different1' does not exist on 'object1'

看游乐场

对象名称

正如 Alex Wayne 所解释的那样:对象将仅具有“作为数据模型的一部分”的名称。

或者换句话说:对象的名称就是您将其关联的对象。

这种联系可能来自内部或外部。 例子:

// Name by self-definition
const someObject = { name: "self-named" };

// Name by association
const nameToObject = { "associated-name": {} };

按名字查找

为避免向对象添加不必要的属性,我将使用“关联名称”方法:

const objects = { object1, object2 };

旁注:在此速记对象初始化中,标识符用作属性名称,它们的引用用作属性值。

有了这个,实现就很简单了:

 const object1 = new class Object1 { get property1() { return "foo"; } get property2() { return "bar"; } }; const object2 = new class Object1 { get different1() { return "asdf"; } get different2() { return "ghjk"; } }; const objects = { object1, object2 }; // The implementation function getPropertyValue(objectName, propertyName) { return objects[objectName][propertyName]; } const value = getPropertyValue("object1", "property2"); console.log(value);

添加打字稿

要添加正确的类型,我们需要考虑输出如何依赖于输入。 而且,输入如何相互依赖。

一个天真的实现可能是这样的:

function getPropertyValue(
  objectName: "object1" | "object2",
  propertyName: "property1" | "property2" | "different1" | "different2"
): any;

但这并没有完全利用 Typescript 的能力。 根据objectName的值,只能使用propertyName的原始类型的子集: propertyName取决于objectName

由于函数参数之间存在依赖关系,因此我们需要一个通用参数来链接它们。

多亏了我们的字典,我们已经知道objectName的哪个值对应于哪个类型。 我们只需要将propertyName限制为该类型的键:

type Objects = typeof objects;

function getPropertyValue<
  K extends keyof Objects
>(
  objectName: K,
  propertyName: keyof Objects[K]
);

调用此函数时,默认会从objectName推断出K ,并根据我们的输入自动限制我们对propertyName的选择。


附录

按标识符命名?

虽然(特别是声明为const的)变量可以引用对象,但它们的标识符(即变量名)通常不被视为其引用对象的名称。 有充分的理由!

您无法通过标识符轻松查找对象。 为此,需要new Functioneval() ,它们以存在安全问题而著称。

因此,请将以下内容视为示范,而非推荐

 const object1 = { name: "Object-1" }; const object2 = { name: "Object-2" }; const o = getObjectByName("object1"); console.log(o); function getObjectByName(name) { // You should avoid new Function; return new Function(`return ${name};`)(); }

for...infor...of ,为了什么?

有多个for循环:

  • 带有初始化、条件和增量块的常规(或简单) for循环。
  • for...of循环遍历可迭代对象。
  • for...in循环遍历可枚举属性

确保您熟悉这些!

提示:在许多情况下,您可以使用Object.keys().entries()而不是for...in循环,这样可以避免与for...of循环混淆。

命名空间污染!

我们不想为每个函数调用实例化objects ,这意味着它需要在该函数之外。

但是天真地将它移到外面会污染命名空间。

相反,我们可以使用闭包来仅实例化它一次,但将其隐藏在全局范围之外:

 // Mock imports const object1 = { property1: "foo", property2: "bar" }; const object2 = { different1: "asdf", different2: "ghjk" }; const getPropertyValue = (() => { const objects = { object1, object2 }; return function(objectName, propertyName) { return objects[objectName][propertyName]; }; })(); const value = getPropertyValue("object1", "property2"); console.log(value); console.log(objects);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM