Changing the type of an existing object is simply not something that can be done in a typesafe manner. The object might be referenced in one place as a MyClass<A> and in another place as a MyClass<B>, with no relationship between types A and B. This can lead to contradictions, for example:
class Transmutation<T> {
constructor(public readonly value: T) {}
transmute<OtherType>(newValue: OtherType): Transmutation<OtherType> {
(this.value as unknown) = newValue;
return this as unknown as Transmutation<OtherType>;
}
}
const t1: Transmutation<number> = new Transmutation(123);
const t2: Transmutation<string> = t1.transmute<string>("foo");
const value: number = t1.value; // has type number, but is actually string "foo"
In contrast, changing the type may be legal if the generic type is variant with respect to this type parameter. For example, an object with actual type ReadonlyList<SubClass> can be safely referenced as a ReadonlyList<BaseClass>.
In most cases, the solution would be to return a new object with a new type instead of changing the type of this. This might be less efficient, but this looks like a programming pattern that should be easy to handle for a garbage collector (for example, by actually reusing the storage of the old object, as you wanted to do directly). Then, your methods would look more like:
create<X>(fn: (_: T) => X): MyClass<X> {
const x = doSomething(fn, this.value);
return new MyClass<X>(x);
}
Some type systems are conceptually able to safely change the type of an object since they can guarantee that the old version of the object is no longer referenced (“affine” type systems that allow each value to be used at most once). TypeScript does not have such a type system.