I need to use some subset of class properties names as values in a map to use inside of the class. In following example I've replaced map by array. The problem is that if property is marked private
it's not listed in keyof
list. How can I specify type of keys if I need to include private names?
var keys: Array<keyof A> = ["x", "y"]; // Error
class A {
private x = 7;
public y = 8;
private keys: Array<keyof A> = ["x", "y"]; // Error
}
There is the same error both for variable outside of the class and for private property inside of it:
Type '"x"' is not assignable to type '"y"'.
The private keyword in object-oriented languages is an access modifier that can be used to make properties and methods only accessible inside the declared class. This makes it easy to hide underlying logic that should be hidden from curious eyes and should not be interacted with outside from the class.
Use closure() to Create Private Properties in JavaScript Using closure() is one of the choices to implement private properties in JavaScript. Inner functions with access to the variables of the surrounding function are known as closures. You can access this link to see the working of this code.
Short answer, no, there is no native support for private properties with ES6 classes. But you could mimic that behaviour by not attaching the new properties to the object, but keeping them inside a class constructor, and use getters and setters to reach the hidden properties.
As you noticed, private
and protected
properties of a class C
do not appear as part of keyof C
. This is usually desirable behavior, since most attempts to index into a class with a private/protected property will cause a compile error. There is a suggestion to allow mapping a type to a version where the private/protected properties are public, which would give you a way to do this... but this feature has not been implemented as of TypeScript 3.5.
So this doesn't work:
namespace Privates {
export class A {
private x: string = "a";
public y: number = 1;
private keys: Array<keyof A> = ["x", "y"]; // Error
}
var keys: Array<keyof A> = ["x", "y"]; // Error
}
const privateA = new Privates.A();
privateA.y; // number
privateA.x; // error: it's private
privateA.keys; // error: it's private
But maybe you don't actually need the properties to be private
, so much as not visible to outside users of the class. You can use a module/namespace to export only the facets of your class that you want, like this:
namespace NotExported {
class _A {
x: string = "a";
y: number = 1;
keys: Array<keyof _A> = ["x", "y"]; // okay
}
export interface A extends Omit<_A, "x" | "keys"> {}
export const A: new () => A = _A;
var keys: Array<keyof _A> = ["x", "y"]; // okay
}
const notExportedA = new NotExported.A();
notExportedA.y; // number
notExportedA.x; // error: property does not exist
notExportedA.keys; // error: property does not exist
In NotExported
, the class constructor _A
and the corresponding type _A
are not directly exported. Internally, keyof _A
contains both the "x"
and "y"
keys. What we do export is a constructor A
and a corresponding type A
that omits the x
property (and keys
property) from _A
. So you get the internal behavior you desire, while the external behavior of NotExported.A
is similar to that of Privates.A
. Instead of x
and keys
being inaccessible due to private
violation, they are inaccessible because they are not part of the exported A
type.
I actually prefer the latter method of not exporting implementation details rather than exposing the existence of private
properties, since private
properties actually have a lot of impact on how the corresponding classes can be used. That is, private
is about access control, not about encapsulation.
Okay, hope that helps; good luck!
Link to code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With