簡體   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