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 at microsoft/TypeScript#22677 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 4.9.
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.
Link to code