Repository: TypeScript
Issue: Brittle circularity inherited from factory-produced class
Labels: Needs Investigation

**TypeScript Version:**  3.8.3

<!-- Search terms you tried before logging this (so others can find this issue more easily) -->

**Search Terms:** extend, from, circular, constructor, class, factory

[The following](https://www.typescriptlang.org/play?#code/MYGwhgzhAECCBO8wE8DKALMAHAptA3gFDTTAD2AdhAC7wCuw1Z8AFFnQEYgCWw0ZAMwBc0MBWQBKAtAC+hOYQpgAtjghYwwPAiRpMuAsWg4AHlmbVoAuhUbdK-AS0EixkwyRLwc1OvAqk4FDGJtQ4FAAmMDooGNh4RJ6e5FS0DEysUolJnhB0uKyCEgDcRkly5UZyCqCQMABKOOTwEXEG2Sk09IzMbJw8fC78HABWTdRZsvKEiipqGlrQjc2t+glGpubwlta21PYBgs7CouJZZdDevv6BdSFhkQ1NzKvxHjmdaT2Z7znQeQVjiULiQKp4wdUZrVggBhZCgHAAOTIERwsHu4SiS2eLTaOAAdEcWFIALwAPmgcIRyNRACEsnJoTAqSAkSicLSMY84IhYmtCU5idByZT4ayaWiGUA) works like a charm. I'm able to define a circular relationship between `RecordShape` and `ArrayShape`. This is wonderful!

```ts
class ArrayShape {
  constructor(public of: any) { }
}

namespace ArrayShape {
  export function of(of: any) {
    return class extends ArrayShape {
      constructor() {
        super(of);
      }
    }
  }
}

class RecordShape {
  constructor(public of: object) { }
}

namespace RecordShape {
  export function of(of: any) {
    return class extends RecordShape {
      constructor() {
        super(of);
      }
    }
  }
}

class CycleNodeA extends RecordShape.of(() => CycleNodeB) {}
class CycleNodeB extends ArrayShape.of(() => CycleNodeA) {}
```

However, this starts to break down as I make it more generic. In the example below, I begin to narrow down the type passed into the `ArrayShape` and `RecordShape` constructors. The result is––once again––circularity errors :/ [here is the playground](https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAFQJ4AcCmB5MAbVAHmQD4AKASkQG8AoRRAJ0yhEaTPABNNgYxMXRAEMAziLCoqYlAG5aAX1q0A9CsRQAFpkQAjMTuCM4AW0QB3TTAibEAAwCCjRsNQBlTcKx2JQuwCVMCDhGLg8vTB9MAA8oTDAuWmFdUSgXaEQIPDFxcKxCADlEGLiE8VTGfgBzABoUEhp6ERS04QzmYS4EAkQwYRNMAC5EAvkGZIq2qCZMTu7UDQwhuUVlNWLY9KhxLR07ZB8jUxFEKpgAN3jEUU8sWiglxAARILgePMxCNw3Srlzbz4Vap1YSSEgNAC8TW+JXif0QH0IQLAtUQ-GAmEY9SaDAA-CgcYhhgJLox5Kp1PFknhMOUATs4DMMcxIDo4LtGOJLNZbMIWQByaaoFiIHh8ASJB5YRAABTg-DijA+iAhiDIlBVDURyNRoNQJAo5PWIFgeHEwBCGg6sBR1wBvkQ6HlYEVdoiiyw4hkMGAGm0Cz5Ol2136mHujw+ABkYABrHSq7VpYESfWIAA+sudio+8ilOgKIRMwjwMAAXphETDYnD-hFo3HIdCfjXMwrMR9Cfiq79xOqqBCGujMQi3FQu99iZhSYThm5c48XsF3gD659uy2o7HMJDnq9lxFCoXi2WKwCvuDyVkcognC53GfkM2ygiV1uGrDn4iAER8u9fuqLm8p51luxAkAA2gAug0dAMB0XT4AsfQDCqiA-s4rhfmMMxzIhHrxigSy4AQhCAfuWCrmBUHkIaygMMEYAVCA0AhGQ6AgLoJYQMUNIDC6HzDMgVCwQw1wgFgjCUE0ShKLQyG0ugbQ6LerjKiJMROow0ygJAsAIIgcDAMQT7wpuDZkJgvHxFAAkoMJhLMKw7CZNkojiB+8IqfeB6kI0omiQxTEsZJ9n+f5ojiZiFlWfxAK0WFiBKP5SWJastBXm5iCBMEoSIo+HniNlIRcEiSYonUZnbu+1afmeX7MDlXD-n5iDgQA0miSBxqghkoJBwxkcBFGgcg7XQfICgwU08HzL0oaofVryhFh02zAhPR5qhaBYMRRAiQw7WdYg3W9cg-W7kuQ2YJRo1teN0k0eS9EIEFUCsexnHWIgfCWWE9KCaFokRRJlDYTJyjyaIikQDoRW5fa6nRJp2ngNA8BIIZxkFVlS0lTqFWvg25A-Xgf0RKIAMtXBLBsEgGXuTV8Jw2T+S+ftAUvWkzFvSFVNhcDUUkyztLxWFKUMCl4PpZziAAMKoFkmAFjwDioczHwAHSGWQIm6MMGoDr0mDmHLCs0srmAAEJSQowmyfTpuKxblsmeIXma9rBsNAIJvy07QEOJQdtAA).

```ts
function TypeOnly<T>() {
  return (undefined as any) as T;
}

// the base from which `ArrayShape` and `RecordShape` extend
abstract class Shape<N extends string, T> {
  abstract readonly name: N;
  abstract readonly type: T;
}

// extracts the `T` from a given shape
type DecodeShape<S extends Shape<string, any>> =
  S extends Shape<string, infer T>
    ? T
    : never;

// for referencing shapes which aren't yet defined
type PointerShape = (() => Shape<string, any>);

// utils for treating shape and pointer shape types as if they are the same
type ShapeLike = Shape<string, any> | PointerShape;
type NormalizeShape<S extends ShapeLike> =
  S extends PointerShape
    ? S extends (() => infer SS) ? SS : never
    : S;
type DecodeShapeLike<S extends ShapeLike> = DecodeShape<NormalizeShape<S>>;

class ArrayShape<T extends ShapeLike> extends Shape<"array", DecodeShapeLike<T>[]> {
  readonly name = "array";
  readonly type = TypeOnly<DecodeShapeLike<T>[]>();

  constructor(public elementShape: T) {
    super()
  }
}

namespace ArrayShape {
  export function of<T extends ShapeLike>(elementShape: T) {
    return class extends ArrayShape<T> {
      constructor() {
        super(elementShape);
      }
    }
  }
}

class RecordShape<T extends Record<string, ShapeLike>> extends Shape<"record", {
  [K in keyof T]: DecodeShapeLike<T[K]>;
}> {
  readonly name = "record";
  readonly type = TypeOnly<{
    [K in keyof T]: DecodeShapeLike<T[K]>;
  }>();

  constructor(public fieldShapes: T) {
    super();
  }
}

namespace RecordShape {
  export function of<T extends Record<string, ShapeLike>>(fieldShapes: T) {
    return class extends RecordShape<T> {
      constructor() {
        super(fieldShapes);
      }
    }
  }
}

const CycleNodeA = RecordShape.of({
  b: () => new CycleNodeB()
}) {}
class CycleNodeB extends ArrayShape.of(() => new CycleNodeA()) {}
```

If the circularity error turns out to be the intended behavior, apologies––I can post on StackOverflow instead. Otherwise, any help would be greatly appreciated! Thank you!