-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow visibility on constructors #2341
Comments
Since all "classes" are actually just functions and since there's no such thing as uncallable function, anywhere the class is visible you can also use the You can however make the class non-public (e.g. in a module) and export an interface for the class. This abuses the fact that interfaces can extend classes but they're not directly instantiable nor extendable. |
Well the same applies to private functions in classes, right? I don't see why we could not get a compile error for accessing a private constructor. |
@billccn I don't like to kill requests by "JS allows then you will not be able to hide". |
Yeah, I guess if the private fields are implemented as a compiler check only, then it can probably be extended to constructors. However, the interface-based workaround already works though. |
👍 There are times when I want to force the programmer to use the factory methods for code readability. The interface-based workaround creates a lot of noise in the code. I think the compiler check only is the way to go. |
Accepted, accepting PRs |
To clarify, could a class with a private constructor be extendable? i.e. Would this throw an error? class A {
private constructor() {
}
}
class B extends A { // Should there be an error at A saying: "Cannot extend private class 'A'"?
} if so, would we then allow this: class A {
protected constructor(a?: any)
private constructor() {
}
}
class B extends A { // No error since 'A' has a non-private constructor
} |
From non-JS developer experience that's expected behaviour. |
In the first example, In the second example, |
See also #471. Is it really required to allow constructors to be private or will protected do? |
@benliddicott it's sometimes useful to force a singleton or to force the programmer to use one of the static methods to create an object, because sometimes it can be more readable. See here. |
@dsherret But you cannot be sure that a downstream user will never have a legitimate need to inherit from your class. The only effect of |
@benliddicott Sometimes the only thing you want is for a class to be unextendable. I refer you to Effective Java Item 15 Minimize Mutability, especially:
Currently there's no |
@billccn , that author's opinion is interesting. So is the idea that the library writer's opinion should override that of the library user. My own experience has been that library writers don't know when to use private, and over-use it, causing headaches for users, simply because they believe they know how their library will be used, when in fact they do not. But rather than a static language like Java, a more apt comparison would be Perl, another dynamic language: http://www.perlmonks.org/?node_id=437623
and:
http://www.perlmonks.org/?node_id=1096925 In fact JavaScript is the same, and - yes - TypeScript is the same, in almost every respect. In typescript you can access private members just fine - using the aptly named escape-hatch: If as a library writer you suspect it is unwise to call a method or inherit from an object, then tell the user it is unwise. But don't prevent them - they may know better than you after all. |
I don't understand the discussion about "protected being sufficient". If TS has the concept of visibilty and I can apply this concept to constructors, the answer is "it is not sufficient". |
If access modifiers are allowed on the constructor then I think it should have consistent behaviour with other modifiers and allow private. Private members in general are for sure useful. They allow you to organize and refactor the implementation details of a class without worrying about causing side effects outside the class. With private constructors I might want to force the developers on my team to program in a certain way. For example, I might want to force them to use the static method here because it's more readable and force them not to extend this class: class Currency {
private constructor(private value: number, private type: CurrencyType) {
}
static fromNumberAndType(value: number, type: CurrencyType) {
return new Currency(value, type);
}
static fromString(str: string) {
const value = ...,
type = ...;
return new Currency(value, type);
}
// ... omitted ...
}
// error:
const badCurrency = new Currency(5.66, CurrencyType.USD);
// ok:
const goodCurrency1 = Currency.fromNumberAndType(5.66, CurrencyType.USD);
const goodCurrency2 = Currency.fromString("5.66 USD"); |
That's a management issue not a language design issue. |
@benliddicott The similar you can say about type descriptors on variables :) If you don't like the feature just use plain JS. OR |
@benliddicott if something is not possible to do then I won't have to send it back when it's wrong after doing a code review. That saves time. Telling the compiler exactly how the code should be used correctly is an asset that gives the proper feedback to the developer using it as they are programming. |
@dsherret No it's an arbitrary constraint that empowers |
@dsherret Not enough to document instead of enforcing yet another constraint? It complicates multiple inheritance significantly, see #4805 (which to me seems like an afterthought right now). I've already expressed some of my thoughts in #3578 so won't bother to do it again. Came out a bit strong with |
@jbondc classes with private constructors wouldn't be able to be inherited. They're essentially sealed and so inheritance, let alone multiple inheritance, shouldn't work with this. It's much nicer to have self-documenting code than to write it externally or in a comment. That's one of the reasons I like all of TypeScript's restrictions and essentially what we're asking for in this feature—just another way to document the code by using the language itself. |
Well here is another way to write your example:
Less OO-ish but you actually have a 'genuinely' real private class. |
@jbondc That's great you have found your way for private classes! Let others use another approach (exportable class with private constructor). Now you can observe that there can be features that satisfies needs of group A and do not disrupt group B work. :) |
+1 |
1 similar comment
+1 |
Is this still on the radar? The PR is hella stale! |
+1 |
1 similar comment
+1 |
+1 |
👍 can be useful for factory design pattern |
Apologies if this is a repeat or just late to the party, but we are using the following pattern to achieve private constructors: interface ExampleBuilder {
Instance(): Example;
}
export interface Example {
Val(): number;
}
export let Example: ExampleBuilder = class ExampleImpl {
constructor(v: number) {
}
Val(): number {
return 42;
}
static Instance(): Example {
return new ExampleImpl(2);
}
};
let x = Example.Instance(); // OK: x has type Example
let y = new Example(5); // ERROR: Cannot use 'new' with an expression whose type lacks a call or construct signature. Note this can be tidied up even more using the recently merged |
@myitcv that's pretty cool to enforce it for now... probably the best way mentioned in my opinion. It's still too verbose though and takes a few seconds to understand what's going on, which is why an access modifier would still be very nice on constructors. Personally, I'm just marking all my future private constructors with |
Agreed. We don't suffer too much from that cognitive load because these classes are code generated. So the interface to the programmer is actually nice and simple. |
fixed by #6885 thanks @AbubakerB! |
Hi, is this feature going to be released in some future version of typescript? Ahh, nevermind. Just found out the road map that states this feature will be included in typescript 2.0. |
Also the milestone being set to |
I am using TypeScript 2.0.2 RC and still get TS1089 when I try to make a |
it is working for me. make sure your command alias is pointing to the right version, and that your editor is updated to use the latest TS version. |
I found the problem. It was the fault of For anyone else having this problem, my solution was to edit my
|
I think it is a pretty common pattern to have a static factory method to create a class and the constructor of this class being otherwise private so that you cannot instantiate the class unless you use the factory method.
The text was updated successfully, but these errors were encountered: