Accessing static methods from instance in Typescript

As @basarat says, it's just a design decision, not a technical limitation.

Actually, even though it's not possible to access test.getTest() just like you would in Java or C#, there are ways to access it:

Both Object.getPrototypeOf() (replacement to the now deprecated Object.prototype.__proto__) or Object.prototype.constructor should work:

Object.getPrototypeOf(test).constructor.getTest();

test.constructor.getTest();

Actually:

Object.getPrototypeOf(test).constructor === test.constructor; // true

Here you can see the compiled source in action:

class Test {
  static getTest() {
    return this.str;
  }
}

Test.str = 'test';

const test = new Test();

console.log(Object.getPrototypeOf(test).constructor.getTest());
console.log(test.constructor.getTest());

console.log(Object.getPrototypeOf(test).constructor === test.constructor);
console.log(Object.getPrototypeOf(test) === Test.prototype);

Note static properties exist in classes but not in instances.

Therefore, if you want to go from test to its prototype, you should call Object.getPrototypeOf(test), not test.prototype, which is a completely different thing.

The .prototype property only exists in functions and, when instantiating a new object using new and a call to that constructor function (new Test()), will become the newly created object's prototype (the deprecated .__proto__).

In your example:

test.prototype;                     // undefined
Test;                               // class Test { static getTest() { ... } }
Test.protoype;                      // { constructor: class Test { ... } }
Test.protoype.constructor;          // class Test { static getTest() { ... } }
Test.protoype.constructor === Test; // true
test.constructor;                   // class Test { static getTest() { ... } }
test.constructor === Test;          // true

Object.getPrototypeOf(test) === Test.prototype; // true

but I'd still like to know why.

Less magic. Static properties exist on the Class not instances. Its clear exactly what is getting called from the code instead of magic binding to class or member function at runtime.

Is it due to technical limitations of Javascript/Typescript, or is this a design decision by the developers of Typescript

Not a technical limitation. A design decision. More to align with ES6 than anything else : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static But over there its a decision made for less magic