[英]Typescript - How to access a variable object property from a variable object
我在尝试编写一个接受两个输入的函数时遇到了一些困难:
并打印该对象的该属性的值。 然而,这些对象都有不同的属性,它们并不完全相同。
对象如下所示:
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 Function
或eval()
,它们以存在安全问题而著称。
因此,请将以下内容视为示范,而非推荐:
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
,为了什么? 有多个for
循环:
确保您熟悉这些!
提示:在许多情况下,您可以使用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.