Typescript: getter 和 setter 的不同访问修饰符

创建于 2015-04-21  ·  40评论  ·  资料来源: microsoft/TypeScript

是否可以为 getter 和 setter 实现不同的访问修饰符? 这样,setter 可以是私有的/受保护的,而 getter 可以是公共的。 在某些情况下,当值应该是只读的时,这非常有用。

例子:

class MyClass {
    private _myProp: any;
    private set myProp(value: any) {
        this._myProp = value;
    }
    public get myProp(): any {
        return this._myProp;
    }
}
Declined Suggestion Too Complex

最有用的评论

无论如何为此添加我的+1,以防将来再次考虑...

所有40条评论

12

由于 #12 以 #6532 关闭,现在看来这个问题超出了范围。
您现在有实施不同访问修饰符的计划吗?
因为readonly不足以解决这里描述的问题,因为属性实际上可能在类中是可写的。

我相信我也应该把我的例子从相关问题移到这里:

declare abstract class Emitter {
    new (): Emitter;
    on: (name:string, handler: (arg:any) => void) => void;
    off: (name:string, handler: (arg:any) => void) => void;
    protected emit: (name:string, arg:any) => void;
}

class Person extends Emitter {
    constructor(name:string) {
        super();
        this.name = name;
    }

    private _name:string;
    get name() {
        return this._name;
    }
    set name(value) {
        if (this._name !== value) {
            this._name = value;
            this.emit('change:name', value);
            //this way is better
            this.updatedAt = new Date();
            //than this way
            this.setUpdatedAt(new Date());
        }
    }

    ////
    private _updatedAt:Date;
    get updatedAt() {
        return this._updatedAt;
    }
    private set updatedAt(value) { //Getter and setter do not agree in visibility
                //some logic and a simple readonly (our absence of a setter) is not enough
        if (this._updatedAt !== value) {
            this._updatedAt = value;
            this.emit('change:updatedAt', value);
        }
    }

    //// method implementation - but what's the point in it?
    private setUpdatedAt(value) {
        if (this._updatedAt !== value) {
            this._updatedAt = value;
            this.emit('change:updatedAt', value);
        }
    }
}

const entity = new Person('Mike');
entity.on('change:updatedAt', console.log.bind(console));
entity.name = 'Thomas';
//but manually setting updatedAt should be forbidden
entity.updatedAt = new Date(); //restricted

在这里,属性updatedAt实际上可以有一个设置器,但它不应该Person之外访问。 此外,这个 setter 包含复杂的逻辑,简单的只读或没有 setter 是不够的。

我同意私有 setter 只是具有附加逻辑(如发射)的私有方法的语法糖,但我认为当与字段相关的部分逻辑在属性(getter)中而另一个在方法中(私有 setter)时,它是不一致的.

这种方式 getter/setter 是在C#中实现的,我认为在编译时允许不同的可见性会很有用,尽管这种合同会在运行时丢失。

这里的建议是使用公共只读 getter 和私有/受保护的支持字段。

访问器与类型系统中的属性是对称的。 我们所做的任何事情都需要体现在类型中并且可以在属性上表达。 添加新的访问修饰符以启用 private_set/public_get 会增加语言的复杂性和学习曲线,并且从中获得的价值与增加的复杂性不匹配。

无论如何为此添加我的+1,以防将来再次考虑...

我还是想看看这个。 C# 拥有它,它在许多情况下都非常有用,尤其是当您有想要从类外部读取的标志,但只能在内部使用私有/受保护设置时。

我认为不经常使用它的建议是一场闹剧,因为该模式无法使用,因为它无法使用。

这是对语言的一个很好的补充:这会增加的复杂性(从打字稿程序员的角度来看)很小。 这个概念很容易理解,编译器应该能够提供很好的错误消息,提示您为什么由于作用域而无法访问 getter 或 setter。

我同意上述用户的观点,这是大多数其他语言的标准。 反对它的论点不成立,应该重新考虑实施。

实际上,我很惊讶您不能在 TS 中执行此操作...对该问题 +1。
这不应该增加学习曲线

private get x() { ... }
public set x(value) { ... }

海事组织,如果你能读懂英文,这里的私有(受保护)/公共意味着什么是不言自明的。 此外,如果您首先定义访问器 - 您可能已经知道它们的用途。

Ps 关于错误:“Getter 和 setter 访问器的可见性不一致” - 好吧:这正是我希望他们做的

以下是两个方便的用例:

Backbone.js,避免丑陋的.get().set()调用:

class Whatever {
    public get rotation(): number {
        return this.get('rotation');
    }
    private set rotation(rotation: number) {
        this.set('rotation', rotation);
    }
}

子类可以修改的属性:

class ExtendMe {
    public get someProperty(): string {
        // some stuff
    }
    protected set someProperty(prop: string) {
        // some stuff
    }
}

我肯定希望看到这个,因为自从我开始使用 TypeScript 以来,它就一直困扰着我。

我同意要求此功能的人。 我刚刚尝试使用公共 get() 方法,并惊讶地发现我的 set 和 get 必须在可见性上达成一致。

我一直在读你们声称它太复杂了。 记住“C++ 方式”为什么它不同于:

private _myAttribute: string;
get myAttribute(): string {...}
setMyAttribute(value: string) {...}

我可以看到很多用例,这就是我现在在这里的原因。
如果我希望 myAttribute 可公开访问但只允许在其类中进行修改怎么办?
如果我想在每次修改属性时添加一些自定义逻辑怎么办?
它可以是一个业务规则,它可以只是一个日志,以了解为什么它被分配了一个特定的值,以及一个用于调试目的的断点条件等等......

基本上,我们希望每次修改属性时都使用一个方法以及语法糖,这使得我们看起来只是在分配一个值,而不是调用一个方法。
C# 对此有属性的概念,我认为 TS 继承了这个概念,但不允许不同的可访问性对于像我一样思考并使我们退回到“C++ 风格”的人来说是一个很大的限制。

“这太难了”绝不应该成为不实现一个非常有用的功能的理由。

添加我的 +1。 在我看来很奇怪,在一种在很多方面都如此完整的语言中,你不能做到这一点。

惊讶地发现这个问题被标记为已关闭。 社区希望此功能。 请重新打开。
+1

12 没有解决这个问题,所以这个问题不应该用引用该问题的评论来结束。

恕我直言,这是一个不错的功能,但据我所知,这只是一种情况“好吧,我不想每次都写 _property,而是想使用私有集属性”

如果可以选择为 TypeScript 核心代码本身增加巨大的复杂性(请记住,更多不必要的复杂性 - 开发速度降低,调试、编写测试的时间更多,最终 - 发布频率降低)但减少写入 1 个符号或写入那1个符号并处理好...我宁愿每次多写1个符号。 最后,我不是 TS core 的贡献者,我绝对不想处理这种糖可能的复杂性。

这不是一个破坏交易的家伙。 此外,发现请求过于复杂而无法实现(结果会增加整体代码复杂性)需要勇气和智慧,因此向开发 TypeScript 的你们致敬,感谢你们的工作!

(ps来这里学习为什么不 - 最后改变了我的想法并将处理this._property = ...
并且仍然会很开心)

@idchlife set属性的目标不是在分配价值时为开发人员节省一个下划线。 您可能还想在set过程中添加一些逻辑。

请重新打开此问题。 这显然是一个非常需要的功能。

+1 我希望看到重新考虑这一点。

很奇怪,当许多其他语言已经这样做时,您的理由是“这会使事情变得混乱”。

由于所有的 👍 都没有做太多,所以我只会再发一条 👍 评论。

这将是语法糖,有助于使事情更具可读性和快速实现。 许多语言都有语法糖,包括 Typescript。 似乎没有任何真正好的理由不去做,过于复杂也不是不做某事的好理由,它更像是一种懒惰的逃避。 解决过于复杂的问题是大多数人被软件工程所吸引的原因之一。 所以解决这个复杂的问题,并以一种不太复杂的方式解决它。 它是否需要重构和有风险的更改? 也许,但事情就是这样完成的。

还是可取的。 其他语言在支持 get- 和 set- 功能的不同可见性方面没有问题。 雪上加霜的是,Typescript 是由微软制作的,就像 C# 一样,但后者完全支持它:

public string myPropertyWithLimitedAccess { get; private set; }   

看那个。 可读性很好,完全没有什么复杂的。

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/auto-implemented-properties

没有理由不支持:

private _myProperty: string;

public get myProperty(): string { return this._myProperty; }
private set myProperty(value: string) { this._myProperty = value; }

奇怪的设计决策的加分点,因为 Typescript 主要是为 .NET 开发人员制作的,以便更习惯于使用浏览器前端。

+1
Typescript 肯定会从这个功能中受益!

即使在 C# 之后,TypeScript 也很棒,但这个毫无意义的限制经常让我恼火。 请重新打开它。

会增加语言的复杂性

readonly [P in keyof T]: T[P]之类的东西相比,这真的这么复杂吗?

撞。 每个人都想要这个功能。 TypesScript 社区不应该做出决定吗?

会增加语言的复杂性

readonly [P in keyof T]: T[P]之类的东西相比,这真的这么复杂吗?

复杂性与其他语言功能的交互有关。 不幸的是我找不到它,但 IIRC RyanCavanaugh 举了一个例子,这个特性可以让你设置,然后使用类表达式通过继承来违反不变量。 仅仅因为编写某个声明很容易,这并不意味着它总是很容易推断它如何影响事物。

问题是给定功能将解决哪些问题,以及它会产生哪些问题。 前者很容易回答,后者却出奇的难回答。 不幸的是,TS 团队有时似乎用“OMG 复杂性”来回应,而不是说明问题。 (公平地说,他们第 n 次回答查询 x 所花费的时间越多,他们开发的时间就越少。)

我不是 100% 同意“社区不应该做出决定”的概念,因为如果在实际开发语言的专家之间达成共识,那应该会告诉你一些事情。 但我认为对于这样的要求,团队对权衡是什么以及他们为什么反对它的深思熟虑的解释并不是太多要求。

就个人而言,我认为在这种情况下,权衡是 1000% 值得的。 但如果我懒得去规范它并让它发挥作用,我想我没有太多抱怨的权利。

现在是重新审视这个问题的好时机吗?

readonly修饰符很棒,但是在很多情况下,您希望能够更改类内部的值,并且仍然让外部的所有内容都是只读的。

我经常选择不创建属性readonly ,因为我不喜欢私有支持字段 + 属性添加的噪音。

如果有一些语法糖可以为您做到这一点,那就太好了。 就像是:

// Option 1: C# style
public name: string { get; private set; }

// Option 2: Swift style
private(set) name: string

// Option 3: Swift struct-style
public readonly name: string

mutating changeName(name: string) {
  this.name = name
}

// Option 4: New keyword
public frozen name1: string
public readonly name2: string

我喜欢选项 2,因为它非常适合 TypeScript 语言。

使用选项 3,您只能更改标记为mutating的函数中的只读字段

使用选项 4, frozen只能在构造函数中设置, readonly可以在此类内部设置,不能由任何外部类或从此类继承的类设置。

作为参考, @yvbeek关于更灵活修饰符的想法更适合在https://github.com/microsoft/TypeScript/issues/37487 中进行讨论。

这个问题是专门针对 getter 和 setter 的,并且有超高的投票数! 我认为给 getter 和 setter 提供不同的访问修饰符会很有用(如果 TypeScript 团队认为它是可以接受的,我们可以在 #37487 中更新现有的修饰符集)

我不是 100% 同意“社区不应该做出决定”的概念,因为如果在实际开发语言的专家之间达成共识,那应该会告诉你一些事情。 但我认为对于这样的要求,团队对权衡是什么以及他们为什么反对它的深思熟虑的解释并不是太多要求。

@snarfblam不要用不相关的评论堵塞这个帖子,但我认为你已经揭示了政府运作方式的核心原则。

没有这个功能对我来说是一个真正的痛苦,并且(除其他外)让我从 TS/NodeJS 切换到更多的东西......类型安全。 这一切都很好,但当你在处理一个包含大量数据结构(深度嵌套很多次)的复杂项目并且你无法正确建模数据时,你会觉得这不是一种“大语言”男孩们”。

在我的特殊情况下,我希望该属性是只读的,但可以从内部修改......并且还可以序列化为 JSON。 太多的箍跳过去。

此功能可能遵循与_可选链接_相同的路径。 人们会要求这个功能多年,然后最终将它添加到语言中,因为它很实用,其他语言也提供相同的功能。

否则我希望一些实现将成为 EcmaScript 的一部分,然后进入 TypeScript。

我最近刚改用打字稿,我一直很喜欢这种语言。 我真的很沮丧,这个在其他语言中存在的实用功能在这里没有实现。 无论您认为它可能会给语言增加多少复杂性,请重新考虑在未来的版本中添加它。

我用 getter 实现了类似于 c# 的东西:

export class I18nService {
  private static ref: I18nService;

  public static get instance(): I18nService {
    if (!I18nService.ref) {
      I18nService.ref = new I18nService();
    }

    return I18nService.ref;
  }
}

像下面这样的类型错误很容易理解,并不复杂:

Property 'foo' is writable only in protected scope within class 'Blah' and its subclasses.

或者

Property 'foo' is readable only in protected scope within class 'Blah' and its subclasses.

等等,与private类似。

老实说,这并不复杂。

顺便说一句,我也偶然发现了这个问题,同时我正在使用这种“hack”:

// somewhere.ts
declare global {
    type Writable<T> = { -readonly [P in keyof T]: T[P]; }
}

// example.ts
class Example {

    public readonly prop: number;

    public doSomething(n: number): void {
        (this as Writable<this>).prop = n;
    }
}

从技术上讲,这可以在任何地方使用,但使用此解决方法应仅限于类方法内的代码。

顺便说一句,我也偶然发现了这个问题,同时我正在使用这种“hack”:

我自己也玩过这个想法,但问题是你无法区分“公共只读”和真正的只读属性。 这使得它不太适合作为通用解决方案。

此页面是否有帮助?
0 / 5 - 0 等级