It's worth noting that when it comes to the private
and protected
properties of a class called C
, they are not included in the list of keys returned by keyof C
. This is intentional, as trying to access a private or protected property in this way would result in a compilation error. There has been a proposal on microsoft/TypeScript#22677 to enable mapping a type where these private/protected properties are made public, but as of TypeScript 4.9, this feature has not yet been implemented.
As demonstrated below, attempting to index into a class with private properties will cause errors:
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: private property
privateA.keys; // error: private property
However, if the goal is simply to hide certain properties from external users rather than making them explicitly private, a module or namespace can be used to selectively export only desired facets of the class:
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 not accessible
notExportedA.keys; // error: property not accessible
In the NotExported
section, the internal details of the constructor and type _A
are kept private. Only the necessary parts are exported through A
. This approach maintains the desired behavior internally while limiting external visibility, similar to how Privates.A
works. It focuses on not exposing implementation details rather than marking properties as private, recognizing that privacy in classes is more about access control than encapsulation.
Link to code