TypeScript 允许在属性访问器(getter/setter)中使用此参数,

eaf3rand  于 10个月前  发布在  TypeScript
关注(0)|答案(7)|浏览(93)

建议

允许在属性访问器中使用 this 参数:

  1. const extension = {
  2. get bar(this:ObjectWithFoo):string {
  3. return this.foo;
  4. }
  5. }

🔍 搜索词

getter setter accessor this argument parameter

✅ 可实现性检查清单

我的建议满足以下指导原则:

  • 这不会对现有的 TypeScript/JavaScript 代码造成破坏性的改变
  • 与函数一样,访问器的 this 参数是可选的,并且默认为 home 对象。
  • 这不会改变现有 JavaScript 代码的运行时行为
  • 与大多数 TypeScript 一样,这个提案对发出的 JavaScript 没有影响。
  • 这可以在不根据表达式的类型发射不同的 JS 的情况下实现
  • 如下面的示例所示,这个提案在技术上已经发射了功能正常的 JavaScript。这个提案只会影响类型检查(就像函数一样)。
  • 这不是一个运行时特性(例如库功能、带有 JavaScript 输出的非 ECMAScript 语法、JS 的新语法糖等)
  • 这个提案只会影响编译期间的类型检查。
  • 这个特性会与 TypeScript's Design Goals 的其他部分一致。
  • 虽然这个特性与所有的设计目标一致,但它最符合目标 5 和 9:“产生一种可组合且易于推理的语言”以及“使用一致的完全可擦除的结构化类型系统”。

⭐ 建议

目前可以给方法分配一个 this 参数来确保其正确使用。例如:

  1. interface ObjectWithFoo {
  2. foo: "foo";
  3. };
  4. const extensions = {
  5. baz(this:ObjectWithFoo):string {
  6. return this.foo + "baz";
  7. },
  8. };
  9. const target:ObjectWithFoo = {
  10. foo: "foo",
  11. };
  12. const extendedTarget = Object.assign(target, extensions);
  13. extendedTarget.baz(); // "foobaz"

当定义将用于扩展其他类型的对象时,这种能力非常有用。
然而,有一个明显的缺失功能。尽管可以将属性访问器添加到扩展对象中,但它们无法访问 this 引用,即使获取器和设置器访问器本质上只是属性描述符上的 getset 函数。这严重限制了它们的功能,尽管在 JavaScript 中得到了完全的支持。

  1. const extensions = {
  2. // The following accessor currently produces:
  3. // Error: 'get' and 'set' accessors cannot declare 'this' parameters.(2784)
  4. get bar(this:ObjectWithFoo):string {
  5. return this.foo;
  6. },
  7. baz(this:ObjectWithFoo):string {
  8. return this.foo + "baz";
  9. },
  10. };

📃 激励性示例

考虑以下简单的扩展框架代码。它使用了 getOwnPropertyDescriptordefineProperty,这些保留了原始的访问器函数(即获取器/设置器)。JavaScript 在访问器的上下文中评估 this 引用,允许任何属性获取器/设置器逻辑表现得好像这些属性是从目标对象一开始就定义的。

  1. function extend<T, E>(target:T, extension:E): T & E {
  2. // Please note that this function is offered as a reasonable stand in for an extension framework.
  3. // It is not, on its own, a complete solution that handles all edge-cases.
  4. for (const extensionName in extensions) {
  5. const extensionDescriptor = Object.getOwnPropertyDescriptor( extensions, extensionName );
  6. if (!extensionDescriptor) throw Error("Missing property descriptor for: " + extensionName);
  7. Object.defineProperty(target, extensionName, extensionDescriptor);
  8. }
  9. return target as T & E;
  10. }

TypeScript 对属性访问器上的 this 支持将允许扩展框架作者表达 JavaScript 的强大功能并确保他们的代码正确。

  1. interface ObjectWithFoo {
  2. foo: "foo";
  3. };
  4. const extensions = {
  5. // Proposed:
  6. get bar(this:ObjectWithFoo):string {
  7. return this.foo;
  8. },
  9. baz(this:ObjectWithFoo):string {
  10. return this.foo + "baz";
  11. },
  12. };
  13. const target:ObjectWithFoo = {
  14. foo: "foo",
  15. };
  16. const extendedTarget = extend(target, extensions);
  17. extendedTarget.bar; // "foo"

除了方便地访问预期的 this 引用外,TypeScript 还可以像目前为函数提供的功能一样为其属性提供相同的类型安全性。

  1. // Current:
  2. extensions.baz(); // The 'this' context of type '...' is not assignable to method's 'this' of type '...'
  3. // Proposed:
  4. extensions.bar; // The 'this' context of type '...' is not assignable to accessor's 'this' of type '...'

检查预期的 this 引用对于属性访问器可以用来帮助防止常见的错误,例如以下情况:

  1. Object.assign(target, extensions);

在这里,工程师本打算附加 bar 属性访问器函数,但意外触发了属性值的求值。而不是分配一个动态获取器,目标收到了一个具有简单固定值的新属性(或潜在的运行时错误)。
一个演示提议代码(实际上可以运行,尽管有 TS 错误!)的Playground链接。

hmtdttj4

hmtdttj41#

FWIW,我不认为在getter上使用this类型会阻止Object.assign错误。就类型检查器而言,你的访问器只是普通的数据属性,所以在这之前需要先进行更改,编译器才能捕获到这个错误。

nkcskrwz

nkcskrwz2#

相关问题/错误报告: #39254

fjnneemd

fjnneemd3#

我目前正在开发一个区块编辑器,所以请不要问我为什么区块有这么多分布类。你知道很多分布类支持方法(this:Block),但是get参数(this:Block)会报错。
#39254 这是我三年前在做一个低代码项目时遇到的问题,现在的语法不再支持这个语法。
这个语法曾经被支持过,但两年前你改变了规则。太多的get参数写在了主区块类中,因为语法不再支持。

  1. export interface Block extends Block$Seek { }
  2. export interface Block extends Block$Event { }
  3. export interface Block extends Block$Anchor { }
  4. export interface Block extends Block$LifeCycle { }
  5. export interface Block extends Block$Operator { }
  6. export interface Block extends Block$Board { }
  7. export interface Block extends Mix { }
  8. Mix(Block, Block$Seek, Block$Event, Block$Anchor, Block$Board, Block$LifeCycle, Block$Operator)

属性访问器可以被认为是一种计算属性,本质上与方法相同,那么为什么支持方法,而不支持属性访问器呢?
属性访问器(getter/setter)和方法处于同一个领域,通常不会分开。现在我不得不将它们分开,因为语法不再支持它们。你能解释一下为什么不支持它们吗?因为支持这个会产生其他问题。我认为ChatGPT会同意允许在属性访问器(getter/setter)中使用这个参数。

0ve6wy6x

0ve6wy6x4#

我认为ChatGPT会批准允许在属性访问器(getter/setter)中使用此参数。

yvgpqqbh

yvgpqqbh5#

你能解释一下为什么不支持它们吗?
All features start at minus 100
我认为ChatGPT会批准的
Please stop 🥲

4smxwvx5

4smxwvx56#

我绝对恳求GitHub上的人们停止用ChatGPT想要的方式来表述开发者的期望。我们不要这样做。
等等,这现在真的成了一个问题吗?我对这个感到非常困惑,这完全不合逻辑,我觉得这是一个笑话,但我没有理解。

pcww981p

pcww981p7#

我有一个非常类似的用例。getter和setter是通过"原型"传递的,而Object.defineProperties用于在对象示例化后将它们附加到目标对象上。在这种情况下,this 不是原型的周围上下文,而是它被复制到的对象的上下文。
此外,它还可以与标准函数一起使用(通过ThisType<>指定this),并且从外部来看,getter和setter基本上是具有预定义参数的函数以及调用它们的语法快捷方式(我知道还有其他功能,但从语义的Angular 来看,这就是本质)。这从Object.defineProperties中很明显,其中可以将任何函数指定为getter或setter。请注意,我们正在谈论更新getter和setter函数的定义-而不是它们的调用(我不是建议说我应该能够说obj.getter.call(this)或类似的东西)。
尽管关于这是否可能、有效或可取的相关票据中实际进行了往返讨论,但这是一个完全有效的JS用例,mixins等也非常常见地使用它,能够在Typescript中准确地建模它们将具有与建模其他JS范式一样的优势。
如果我想尝试实现这个,TS开发人员能指给我正确的方向吗?

相关问题