简体   繁体   English

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

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

I'm having some difficulty trying to write a function that takes two inputs:我在尝试编写一个接受两个输入的函数时遇到了一些困难:

  1. The name of an object对象名称
  2. The name of a property物业名称

and prints the value of that property for that object.并打印该对象的该属性的值。 However, the objects all have different properties, they're not all the same.然而,这些对象都有不同的属性,它们并不完全相同。

The objects look like this:对象如下所示:

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

Here's what I'm tried so far:到目前为止,这是我尝试过的:

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

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

This didn't work, came back as undefined.这没有用,返回为未定义。 It seems like index is being calculated correctly, but it doesn't seem like objects[index][propertyName] is properly accessing the object's value.似乎 index 计算正确,但objects[index][propertyName]似乎没有正确访问对象的值。 Though weirdly, when I tried the following, it ALMOST worked:虽然很奇怪,但当我尝试以下操作时,它几乎起作用了:

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

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

Here I actually got it to print the correct value, but the problem is that since I'm just iterating over all the objects in a for loop, it tries to print the value for all objects, instead of just the one that matches the objectName.在这里我实际上让它打印了正确的值,但问题是因为我只是在 for 循环中遍历所有对象,它会尝试打印所有对象的值,而不仅仅是与 objectName 匹配的对象. So it prints the correct value for the object that has the property I'm trying to access, but then gets undefined for the other object which does not have that property.所以它为具有我试图访问的属性的对象打印正确的值,但是对于没有该属性的另一个对象则为未定义。

I suppose I could add some property to each object called name and do something like this:我想我可以为每个名为name的对象添加一些属性并执行如下操作:

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

But I'd rather not add unnecessary properties to these objects if I can help it.但如果可以的话,我宁愿不向这些对象添加不必要的属性。

Is there a better way to do this?有一个更好的方法吗?

Objects can be referenced by a variable, but and object has no quality of being named unless your code explicitly provides that as part of your data model.对象可以被变量引用,但是对象没有被命名的特性,除非您的代码明确地将其作为数据模型的一部分提供。

For instance:例如:

const obj = { abc: 123 }

Here the value { abc: 123 } is not named obj .这里的值{ abc: 123 }没有命名为obj There is a variable with an identifier of obj that references the value that is { abc: 123 } .有一个标识符为obj的变量引用了{ abc: 123 }的值。

What this means in practice is that if you only have a reference to { abc: 123 } , then you cannot know what other variable identifiers also hold a reference to this value.这在实践中意味着,如果您只有对{ abc: 123 }的引用,那么您无法知道还有哪些其他变量标识符也持有对该值的引用。

And you cannot get a variable's value by its identifier as a string.而且您不能通过标识符作为字符串来获取变量的值。

So this cannot possibly work:所以这不可能工作:

const foo = 'bar'

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

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

But what you can have is an object with properties.但是您可以拥有的是具有属性的对象。 Properties have keys which are specific strings.属性具有特定字符串的键。

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

Which is shorthand for:这是以下内容的简写:

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

You now have object with properties that correspond to the names you want to use for your objects.您现在拥有的对象的属性与您要用于对象的名称相对应。


Lastly, let's type your function properly:最后,让我们正确输入您的函数:

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

This function is now generic, which means it accepts a type.这个函数现在是通用的,这意味着它接受一个类型。 In this case it accepts the object name you want to use as T , which any keyof the objects object.在这种情况下,它接受您要用作T的对象名称,它是objects对象的任何keyof

Then the propertyName argument uses whatever key that was to find out what properties should be allowed, by constraining the type to the properties of just one of those objects.然后propertyName参数使用任何键来找出应该允许的属性,方法是将类型限制为这些对象之一的属性。

Now to get the data you drill into objects by the name of a property on objects , and then again on the specific property name.现在要获取数据,您可以通过objects上的属性名称钻取objects ,然后再次钻取特定属性名称。

Usage looks like this:用法如下所示:

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

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

See Playground 看游乐场

Object names对象名称

As Alex Wayne explains : Objects will only have names "as part of your data model."正如 Alex Wayne 所解释的那样:对象将仅具有“作为数据模型的一部分”的名称。

Or in other words: An object's name is what you associate it with.或者换句话说:对象的名称就是您将其关联的对象。

That association may come from within or from outside.这种联系可能来自内部或外部。 Example:例子:

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

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

Looking up by name按名字查找

To avoid adding unnecessary properties to your objects, I'll use the "name by association" approach:为避免向对象添加不必要的属性,我将使用“关联名称”方法:

const objects = { object1, object2 };

Sidenote : In this shorthand object initialization , the identifiers are used as property names and their references as the properties' values.旁注:在此速记对象初始化中,标识符用作属性名称,它们的引用用作属性值。

With this, an implementation is straightforward:有了这个,实现就很简单了:

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

Adding Typescript添加打字稿

To add proper typing, we need to think about how the output depends on the inputs.要添加正确的类型,我们需要考虑输出如何依赖于输入。 But also, how the inputs depend on each other.而且,输入如何相互依赖。

A naive implementation may look like this:一个天真的实现可能是这样的:

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

But this doesn't fully use what Typescript is capable of.但这并没有完全利用 Typescript 的能力。 Only a subset of propertyName 's original types can be used depending on objectName 's value: propertyName depends on objectName .根据objectName的值,只能使用propertyName的原始类型的子集: propertyName取决于objectName

Since there is one dependency between function parameters, we need a generic parameter to link them.由于函数参数之间存在依赖关系,因此我们需要一个通用参数来链接它们。

We already know which value of objectName corresponds to which type, thanks to our dictionary.多亏了我们的字典,我们已经知道objectName的哪个值对应于哪个类型。 We only need to restrict propertyName to that type's keys:我们只需要将propertyName限制为该类型的键:

type Objects = typeof objects;

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

When calling this function, K will be inferred from objectName by default, and automatically restrict our options for propertyName according to our typing.调用此函数时,默认会从objectName推断出K ,并根据我们的输入自动限制我们对propertyName的选择。


Addendum附录

Name by identifier?按标识符命名?

While (especially const declared) variables can reference objects, their identifier –that is the variable name– is usually not considered their referenced object's name.虽然(特别是声明为const的)变量可以引用对象,但它们的标识符(即变量名)通常不被视为其引用对象的名称。 For good reason!有充分的理由!

You cannot easily look up objects through an identifier.您无法通过标识符轻松查找对象。 To do so requires either new Function or eval() , which are known for having security issues .为此,需要new Functioneval() ,它们以存在安全问题而著称。

Therefore, please see the following as a demonstration, not recommendation :因此,请将以下内容视为示范,而非推荐

 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...in , for...of , what for? for...infor...of ,为了什么?

There are multiple for loops:有多个for循环:

  • The regular (or simple) for -loop with an initialization, condition and incrementation block.带有初始化、条件和增量块的常规(或简单) for循环。
  • The for...of -loop to iterate through an iterable object. for...of循环遍历可迭代对象。
  • The for...in -loop to loop over enumerable properties . for...in循环遍历可枚举属性

Make sure you familiarize yourself with these!确保您熟悉这些!

Tip : In many cases you can use Object.keys() or .entries() instead of a for...in -loop, which avoids confusion with for...of -loops.提示:在许多情况下,您可以使用Object.keys().entries()而不是for...in循环,这样可以避免与for...of循环混淆。

Namespace pollution!命名空间污染!

We don't want to instantiate objects for every function call, which means it needs to be outside that function.我们不想为每个函数调用实例化objects ,这意味着它需要在该函数之外。

But naively moving it outside pollutes the namespace.但是天真地将它移到外面会污染命名空间。

Instead, we can make use of a closure to instantiate it only once, but keep it hidden from the global scope:相反,我们可以使用闭包来仅实例化它一次,但将其隐藏在全局范围之外:

 // 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