Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: The jQuery Community
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: The jQuery Community ( @jquery )

Static Methods Can Access Private Class Constructors In TypeScript

By Ben Nadel on

Yesterday, in a TypeScript lunch-n-learn that Rob Eisenberg was leading here at InVision, we started talking about various ways in which we could make class Constructors impossible to invoke from outside of the Class context. This got me thinking about Static methods; and, their ability to access private instance propertiers. It has already blown my mind that one class instance can access the private variables of another class instance. So, I started to wonder if Static methods were granted the same kind of access. And, if so, could they be used to invoke private constructors on their own class. Spoiler alert: they are and they can.


 
 
 

 
 
 
 
 

The cool thing about the "implements" directive in TypeScript is that it can point to both Interfaces and Classes. Meaning, one Class can be defined as implementing the API of an existing class. However, unlike an Interface, a Class definition can have private properties. And, if you go to implement that Class, you have to include the same private properties in your implementation. If you don't, TypeScript will throw an error.

  • class A {
  • private message: string;
  • }
  •  
  • class B implements A {
  • // ...
  • }

As you can see in this code, Class B is implementing Class A, which has a private property. Now, if we try to run this TypeScript through ts-node, we get the following error:

Unable to compile TypeScript
Class 'B' incorrectly implements class 'A'. Did you mean to extend 'A' and inherit its members as a subclass? Property 'message' is missing in type 'B'. (2720)

At first, this seems counter-intuitive - why would TypeScript even care about private implementation details? After all, isn't that the whole point of encapsulation? That we can change the private implementation as we need to without breaking the API contract?

This is one feature of Class design that really confused me due to my lack of experience with Object Oriented Programming (OOP) languages. It turns out that private variables are technically part of the Class API - but only locally to the Class. What this means is that while no one else outside of the Class can access the private properties or Class "A", one instance of class "A" can actually access the private properties of another instance of Class "A".

Mind blown!

Given this private property accessibility, I wanted to see if Static method on a Class could access the private properties of a Class instance. Specifically, I wanted to see if Static "factory methods" could invoke a private constructor, forcing all Class instantiation to go through the static methods. To test this, I created a Person class with a private constructor and several static factory methods:

  • interface NameParts {
  • first: string;
  • middle?: string;
  • last: string;
  • }
  •  
  • class Person {
  •  
  • private first: string;
  • private middle: string;
  • private last: string;
  •  
  • // I initialize the Person class with the given name.
  • // --
  • // CAUTION: This constructor is PRIVATE - you must use one of the static factory
  • // methods in order to instantiate the class.
  • private constructor( first: string, middle: string, last: string ) {
  •  
  • this.first = first;
  • this.middle = middle;
  • this.last = last;
  •  
  • }
  •  
  • // ---
  • // PUBLIC METHODS.
  • // ---
  •  
  • public toString() : string {
  •  
  • var printableParts = [ this.first, this.middle, this.last ].filter(
  • ( part: string ) : boolean => {
  •  
  • return( !! part );
  •  
  • }
  • );
  •  
  • return( printableParts.join( " " ) );
  •  
  • }
  •  
  • // ---
  • // STATIC METHODS.
  • // ---
  •  
  • // NOTE: Since these "factory methods" are part of the Person class definition, they
  • // are able to access the PRIVATE CONSTRUCTOR. As such, they can act as a proxy for
  • // the instantiation of the Person class.
  •  
  • static create( first: string, middle: string, last: string ) : Person;
  • static create( first: string, last: string ) : Person;
  • static create( first: string) : Person;
  • static create( a: string, b?: string, c?: string ) : Person {
  •  
  • switch ( arguments.length ) {
  • case 1:
  • return( new Person( a, "", "" ) );
  • // @ts-ignore: TS7027: Unreachable code detected.
  • break;
  • case 2:
  • return( new Person( a, "", b ) );
  • // @ts-ignore: TS7027: Unreachable code detected.
  • break;
  • default:
  • return( new Person( a, b, c ) );
  • // @ts-ignore: TS7027: Unreachable code detected.
  • break;
  • }
  •  
  • }
  •  
  • static createFromName( name: string ) : Person {
  •  
  • var parts = name.split( " " );
  •  
  • switch ( parts.length ) {
  • case 1:
  • return( new Person( parts[ 0 ], "", "" ) );
  • // @ts-ignore: TS7027: Unreachable code detected.
  • case 2:
  • return( new Person( parts[ 0 ], "", parts[ 1 ] ) );
  • // @ts-ignore: TS7027: Unreachable code detected.
  • break;
  • case 3:
  • return( new Person( parts[ 0 ], parts[ 1 ], parts[ 2 ] ) );
  • // @ts-ignore: TS7027: Unreachable code detected.
  • break;
  • default:
  • return( new Person( parts[ 0 ], parts[ 1 ], parts.slice( 2 ).join( " " ) ) );
  • // @ts-ignore: TS7027: Unreachable code detected.
  • break;
  • }
  •  
  • }
  •  
  • static createFromParts( parts: NameParts ) : Person {
  •  
  • return( new Person( parts.first, ( parts.middle || "" ), parts.last ) );
  •  
  • }
  •  
  • }
  •  
  • // ----------------------------------------------------------------------------------- //
  • // ----------------------------------------------------------------------------------- //
  •  
  • // Now that we have our class with a private constructor, let's try using the various
  • // Static Methods to create the class.
  • var sarah = Person.create( "Sarah", "Danger", "Smith" );
  •  
  • console.log( "USING: .create()" );
  • console.log( sarah );
  • console.log( sarah.toString() );
  • console.log( "--" );
  •  
  • var tricia = Person.createFromName( "Tricia J. Smith Jones" );
  •  
  • console.log( "USING: .createFromName()" );
  • console.log( tricia );
  • console.log( tricia.toString() );
  • console.log( "--" );
  •  
  • var kim = Person.createFromParts({
  • first: "Kim",
  • middle: "",
  • last: "Smith"
  • });
  •  
  • console.log( "USING: .createFromParts()" );
  • console.log( kim );
  • console.log( kim.toString() );
  • console.log( "--" );

As you can see, the Person class has a private constructor which takes three name components (first, middle, and last). Then, I have several Static methods which provide different ways to invoke the private constructor. And, if we run this code through ts-node, we get the following console output:


 
 
 

 
 Static Methods in TypeScript can access the private constructors of their class. 
 
 
 

As you can see, the Static factory methods on the Person class were all able to access and invoke the private class constructor. In essence, this forces all Person class instantiation through the static methods.

To be clear, I am not recommending that you use this as your primary means for class instantiation. This was strictly an exploration of the access rights that static class methods have on the class definition. And, as it turns out, static methods can access private constructors. This may be an obvious thing for classically trained computer programmers. But, for people like me, this is new and exciting information.



Looking For A New Job?

Ooops, there are no jobs. Post one now for only $29 and own this real estate!

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

Its really an Excellent post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog. Thanks for sharing....

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.