【问题标题】:emberjs glimmer object set() with variable property name具有可变属性名称的 emberjs glimmer 对象 set()
【发布时间】:2020-04-29 13:32:55
【问题描述】:

我在 Ember 3.15 中有一个组件,我正在尝试做类似的事情

import { action, set } from '@ember/object';

@action
someMethod() {
  const value = ... // something random
  let propertyName = ... // some variable string
  set(this, propertyName, value);
}

它在浏览器中似乎工作正常,但打字稿将设置行标记为错误(特别是 propertyName 参数)。那么如果它有效,为什么打字稿不喜欢它呢?

这似乎也发生在 get() 中,它不喜欢像 get(this, propertyName) 这样的变量 propertyNames。

【问题讨论】:

  • 你是说ember 3.15吗?

标签: typescript ember.js glimmer.js


【解决方案1】:

一般来说,如果您的财产是@tracked,则不需要set,只需this[propertyName] = value;即可。

但是,您的问题可能是一般的打字稿限制。实际是静态类型的一个普遍问题:

Typescript 只做静态分析。所以它不会执行你的代码。所以它无法知道动态生成的属性键是否真的存在。

所以如果你有这样的事情:

class Foo {
  data1: number = 1;
  data2: number = 2;
  foo() {
    const fixedProp = 'data1';
    console.log(this[fixedProp]);
    const dynamicProp = 'data' + (1 + 1);
    console.log(this[dynamicProp]);
  }
}

然后 typescript 将无法验证 this[dynamicProp] 是否确实存在,因为为此它需要 执行 'data' + (1 + 1); 所以它会知道 dynamicProp 实际上是什么。所以通过静态分析不可能知道this[dynamicProp]是否存在。

你可以通过(this as any)[dynamicProp] 告诉 typescript 做你想做的事,它会忽略它。 但通常如果您动态计算属性键,则不能依赖静态分析。

【讨论】:

  • 很高兴知道这一点。我从来没有弄清楚如何通过方括号和打字稿使用 keyaccessor,所以谢谢。但是,对于这个特定的用例,我的变量是嵌套的跟踪属性。因为它们是嵌套的,所以据我了解,您必须使用 set() 方法才能让 Ember 魔法工作,因为仅使用 @tracked 不适用于嵌套属性。也许我可以这样设置,然后使用 Ember 的 setPropertyChanged() 方法。下次我做这个项目的时候我会试试的
  • 一些注意事项: 1. 你不应该使用set setPropertyChanged 。相反,您应该重新构建数据建模方式。如果您需要在更改时更新这些属性,请更新整个根状态(这意味着:不要在本地改变它们,在拥有它们的类中进行)或直接在拥有它们的类上使用 @tracked那个状态。 2. 不要为动态道具做this as any。相反,要么在本地定义属性,要么使用 keyof 运算符更具体地了解允许的属性名称。
  • @ChrisKrycho 为什么我应该特别避免设置?对于我的用例,我正在与第 3 方 api 交互,我必须在其中导航大型、复杂和动态的数据结构。我使用@tracked 跟踪所有内容,但其中许多是从第 3 方数据复制的复杂对象。
  • @IanKirkpatrick 使用 set 基本上只对您“有效”,因为它围绕自动跟踪模型工作。我强烈建议阅读this post(Ember Core 团队成员 Chris Garrett 撰写)以了解如何包装其他 API(当然细节会有所不同)。关键是将第三方 API 封装在一个能够正确区分“根”和“派生”状态的系统中,其中根状态被正确地自动跟踪,没有其他人知道或考虑这一点
【解决方案2】:

您所描述的情况有两个基本问题——一个与 TypeScript 相关,一个与 TypeScript 无关。

TypeScript 的问题是 TS 通常知道属性的名称,并且会检查您是否正确设置了内容——无论是在使用普通 JS 属性查找和赋值时,还是在使用 Ember 的 get 和 @987654325 时@ 职能。具体来说,Ember 的类型试图确保您在执行 getset 时不会出现拼写错误。您可以在此示例中看到为什么它们不允许任意字符串:

import Component from '@ember/component';
import { action, set } from '@ember/object';

export default class Whoops extends Component {
  greeting = 'Hello';

  @action updateGreeting(newGreeting) {
    set(this, 'greering', newGreeting);
    //         ----^--- TYPO!!!
  }
}

如果set(或get)的类型只允许任意字符串,TS 在这里根本帮不了你;它会放过它,而您必须自己找出错误,而不是编译器提前告诉您有关它的有用信息。

在你遇到的情况下,TypeScript 大概只看到一个 string,它说“我没有任何方法可以检查这个字符串是否属于该属性。” p>

这里有几种改进方法。首先,如果可以的话,你应该弄清楚是否可以将 propertyName 的类型限制为 keyof 来表示它来自的类型。 (解释keyof 超出了这个答案的范围,this section in the handbookthis blog post 会让你跟上速度。)

不过,第二个问题与更大的问题有关:您在讨论该问题的另一个答案时指出,问题在于您正试图在单个跟踪的根状态上深度设置属性。一般来说,您不应该以这种方式改变自动跟踪状态——它是旧的观察者驱动模式的遗留物,Ember Classic 使用它的计算属性。相反,更愿意通过该状态的所有者驱动对该自动跟踪状态的所有更改。这样你就完全不需要set了,系统会自动正确更新。

您可以通过自动跟踪嵌套状态自身来做到这一点,方法是为它定义一个类,或者使用tracked-built-ins 之类的东西来包装一个普通的JS对象。无论哪种方式,与其从任何地方进入并深度改变该状态,在拥有该状态的对象上进行。如果您遵循该模式,并将propertyName 限制为keyof TheOwnerOfTheState,其中TheOwnerOfTheState 是某个类,那么无论在Ember 端还是TypeScript 端,一切都将“正常工作”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-28
    • 2017-06-29
    • 2022-01-17
    相关资源
    最近更新 更多