Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 65 additions & 3 deletions tsc/internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,13 @@ type VarianceStackEntry struct {
typeParameters []*Type
}

type ResolvingObjectType struct {
t *Type // Type being resolved
mapper *TypeMapper // Instantiation mapper
baseTypes []*Type // Instantiated base types
resolvedMembers ast.SymbolTable // Members resolved by getDeclaredPropertyOfResolvingType
}

const maxSerializationLevel = 2

type Program interface {
Expand Down Expand Up @@ -850,6 +857,7 @@ type Checker struct {
awaitedTypeStack []*Type
reverseMappedSourceStack []*Type
reverseMappedTargetStack []*Type
resolvingObjectTypeStack []ResolvingObjectType
reverseExpandingFlags ExpandingFlags
freeRelater *Relater
subtypeRelation *Relation
Expand Down Expand Up @@ -19381,6 +19389,11 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr
t = c.getReducedApparentType(t)
switch {
case t.flags&TypeFlagsObject != 0:
if t.objectFlags&ObjectFlagsMembersResolved == 0 && len(c.resolvingObjectTypeStack) != 0 {
if symbol := c.getDeclaredPropertyOfResolvingType(t, name); symbol != nil {
return symbol
}
}
resolved := c.resolveStructuredTypeMembers(t)
symbol := resolved.getMember(name)
if symbol != nil {
Expand Down Expand Up @@ -19428,6 +19441,38 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr
return nil
}

// We allow accessing declared members of a class or interface while building its member layout. This
// means that base classes of a class or interface can access members declared in the class or interface
// during its resolution without causing a circularity error. Members resolved by this function are
// eventually propagated into the resolved members of the class or interface.
func (c *Checker) getDeclaredPropertyOfResolvingType(t *Type, name string) *ast.Symbol {
for i := len(c.resolvingObjectTypeStack) - 1; i >= 0; i-- {
if c.resolvingObjectTypeStack[i].t == t {
resolved := c.resolvingObjectTypeStack[i].resolvedMembers.Get(name)
if resolved == nil {
if symbol := c.resolveDeclaredMembers(getTargetType(t)).declaredMembers.Get(name); symbol != nil && c.isNamedMember(symbol, name) {
resolved = c.instantiateSymbol(symbol, c.resolvingObjectTypeStack[i].mapper)
} else {
for _, baseType := range c.resolvingObjectTypeStack[i].baseTypes {
if symbol := c.getPropertyOfTypeEx(baseType, name, true, false); symbol != nil && !isStaticPrivateIdentifierProperty(symbol) {
resolved = symbol
break
}
}
}
if resolved != nil {
if c.resolvingObjectTypeStack[i].resolvedMembers == nil {
c.resolvingObjectTypeStack[i].resolvedMembers = ast.NewSymbolTableWithCapacity(1)
}
c.resolvingObjectTypeStack[i].resolvedMembers.Set(name, resolved)
}
}
return resolved
}
}
return nil
}

// Return the type of the given property in the given type, or nil if no such property exists
func (c *Checker) getTypeOfPropertyOfType(t *Type, name string) *Type {
prop := c.getPropertyOfType(t, name)
Expand Down Expand Up @@ -19574,7 +19619,6 @@ func (c *Checker) resolveObjectTypeMembers(t *Type) {
var mapper *TypeMapper
var typeParameters []*Type
var typeArguments []*Type
var members ast.SymbolTable
var callSignatures []*Signature
var constructSignatures []*Signature
var indexInfos []*IndexInfo
Expand Down Expand Up @@ -19606,6 +19650,7 @@ func (c *Checker) resolveObjectTypeMembers(t *Type) {
instantiatedBaseTypes[i] = c.getTypeWithThisArgument(c.instantiateType(baseType, mapper), thisArgument, false /*needsApparentType*/)
}
}
c.resolvingObjectTypeStack = append(c.resolvingObjectTypeStack, ResolvingObjectType{t: t, mapper: mapper, baseTypes: instantiatedBaseTypes})
// Member layout must be computed before inherited signatures, which may circularly depend on it
layout := c.getMemberLayout(t, instantiatedBaseTypes)
for _, instantiatedBaseType := range instantiatedBaseTypes {
Expand All @@ -19621,12 +19666,29 @@ func (c *Checker) resolveObjectTypeMembers(t *Type) {
return findIndexInfo(indexInfos, info.keyType) == nil
}))
}
c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos)
c.setStructuredTypeMembers(t, nil, callSignatures, constructSignatures, indexInfos)
lazyState := c.lazyStateArena.New()
lazyState.layout = layout
lazyState.mapper = mapper
lazyState.baseTypes = instantiatedBaseTypes
t.AsTypeReference().lazy = lazyState
c.propagateResolvedDeclaredMembers()
c.resolvingObjectTypeStack = c.resolvingObjectTypeStack[:len(c.resolvingObjectTypeStack)-1]
}

// Propagate members resolved by getDeclaredPropertyOfResolvingType
func (c *Checker) propagateResolvedDeclaredMembers() {
r := &c.resolvingObjectTypeStack[len(c.resolvingObjectTypeStack)-1]
s := r.t.AsTypeReference()
for symbol := range r.resolvedMembers.Values() {
if memberInfo := s.lazy.layout.memberInfoMap[symbol.Name()]; memberInfo != nil {
if s.properties == nil {
s.properties = make([]*ast.Symbol, len(s.lazy.layout.memberInfos))
}
s.properties[memberInfo.slotIndex] = symbol
}
}
r.resolvedMembers = nil
}

func (c *Checker) getMemberLayout(t *Type, baseTypes []*Type) *MemberLayout {
Expand Down Expand Up @@ -28117,7 +28179,7 @@ func (c *Checker) getPropertyNameFromIndex(indexType *Type, accessNode *ast.Node
}

func (c *Checker) isStringIndexSignatureOnlyTypeWorker(t *Type) bool {
return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && len(c.getLayoutPropertiesOfType(t)) == 0 && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil ||
return t.flags&TypeFlagsObject != 0 && t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) == 0 && !c.isGenericMappedType(t) && len(c.getLayoutPropertiesOfType(t)) == 0 && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil ||
t.flags&TypeFlagsUnionOrIntersection != 0 && core.Every(t.Types(), c.isStringIndexSignatureOnlyType)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//// [tests/cases/compiler/circularDeclaredPropertyAccess.ts] ////

=== circularDeclaredPropertyAccess.ts ===
// https://github.com/microsoft/TypeScript/issues/64605

interface Internals<I = unknown> { input: I }
>Internals : Symbol(Internals, Decl(circularDeclaredPropertyAccess.ts, 0, 0))
>I : Symbol(I, Decl(circularDeclaredPropertyAccess.ts, 2, 20))
>input : Symbol(Internals.input, Decl(circularDeclaredPropertyAccess.ts, 2, 34))
>I : Symbol(I, Decl(circularDeclaredPropertyAccess.ts, 2, 20))

interface Schema { _zod: Internals }
>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45))
>_zod : Symbol(Schema._zod, Decl(circularDeclaredPropertyAccess.ts, 3, 18))
>Internals : Symbol(Internals, Decl(circularDeclaredPropertyAccess.ts, 0, 0))

type RecordInput<V extends Schema> = V extends unknown ? Record<string, V["_zod"]["input"]> : never;
>RecordInput : Symbol(RecordInput, Decl(circularDeclaredPropertyAccess.ts, 3, 36))
>V : Symbol(V, Decl(circularDeclaredPropertyAccess.ts, 4, 17))
>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45))
>V : Symbol(V, Decl(circularDeclaredPropertyAccess.ts, 4, 17))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>V : Symbol(V, Decl(circularDeclaredPropertyAccess.ts, 4, 17))

interface RecordSchema<V extends Schema> extends Schema { _zod: Internals<RecordInput<V>> }
>RecordSchema : Symbol(RecordSchema, Decl(circularDeclaredPropertyAccess.ts, 4, 100))
>V : Symbol(V, Decl(circularDeclaredPropertyAccess.ts, 5, 23))
>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45))
>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45))
>_zod : Symbol(RecordSchema._zod, Decl(circularDeclaredPropertyAccess.ts, 5, 57))
>Internals : Symbol(Internals, Decl(circularDeclaredPropertyAccess.ts, 0, 0))
>RecordInput : Symbol(RecordInput, Decl(circularDeclaredPropertyAccess.ts, 3, 36))
>V : Symbol(V, Decl(circularDeclaredPropertyAccess.ts, 5, 23))

interface UnionInternals<T extends readonly Schema[]> extends Internals<T[number]["_zod"]["input"]> {}
>UnionInternals : Symbol(UnionInternals, Decl(circularDeclaredPropertyAccess.ts, 5, 91))
>T : Symbol(T, Decl(circularDeclaredPropertyAccess.ts, 6, 25))
>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45))
>Internals : Symbol(Internals, Decl(circularDeclaredPropertyAccess.ts, 0, 0))
>T : Symbol(T, Decl(circularDeclaredPropertyAccess.ts, 6, 25))

interface UnionSchema<T extends readonly Schema[]> extends Schema { _zod: UnionInternals<T> }
>UnionSchema : Symbol(UnionSchema, Decl(circularDeclaredPropertyAccess.ts, 6, 102))
>T : Symbol(T, Decl(circularDeclaredPropertyAccess.ts, 7, 22))
>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45))
>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45))
>_zod : Symbol(UnionSchema._zod, Decl(circularDeclaredPropertyAccess.ts, 7, 67))
>UnionInternals : Symbol(UnionInternals, Decl(circularDeclaredPropertyAccess.ts, 5, 91))
>T : Symbol(T, Decl(circularDeclaredPropertyAccess.ts, 7, 22))

type JsonValue = { [k: string]: JsonValue };
>JsonValue : Symbol(JsonValue, Decl(circularDeclaredPropertyAccess.ts, 7, 93))
>k : Symbol(k, Decl(circularDeclaredPropertyAccess.ts, 8, 20))
>JsonValue : Symbol(JsonValue, Decl(circularDeclaredPropertyAccess.ts, 7, 93))

type _Json = UnionSchema<[RecordSchema<Json>]>;
>_Json : Symbol(_Json, Decl(circularDeclaredPropertyAccess.ts, 8, 44))
>UnionSchema : Symbol(UnionSchema, Decl(circularDeclaredPropertyAccess.ts, 6, 102))
>RecordSchema : Symbol(RecordSchema, Decl(circularDeclaredPropertyAccess.ts, 4, 100))
>Json : Symbol(Json, Decl(circularDeclaredPropertyAccess.ts, 11, 67))

type _JsonInternals = _Json["_zod"];
>_JsonInternals : Symbol(_JsonInternals, Decl(circularDeclaredPropertyAccess.ts, 9, 47))
>_Json : Symbol(_Json, Decl(circularDeclaredPropertyAccess.ts, 8, 44))

interface JsonInternals extends _JsonInternals { input: JsonValue }
>JsonInternals : Symbol(JsonInternals, Decl(circularDeclaredPropertyAccess.ts, 10, 36))
>_JsonInternals : Symbol(_JsonInternals, Decl(circularDeclaredPropertyAccess.ts, 9, 47))
>input : Symbol(JsonInternals.input, Decl(circularDeclaredPropertyAccess.ts, 11, 48))
>JsonValue : Symbol(JsonValue, Decl(circularDeclaredPropertyAccess.ts, 7, 93))

interface Json extends _Json { _zod: JsonInternals }
>Json : Symbol(Json, Decl(circularDeclaredPropertyAccess.ts, 11, 67))
>_Json : Symbol(_Json, Decl(circularDeclaredPropertyAccess.ts, 8, 44))
>_zod : Symbol(Json._zod, Decl(circularDeclaredPropertyAccess.ts, 12, 30))
>JsonInternals : Symbol(JsonInternals, Decl(circularDeclaredPropertyAccess.ts, 10, 36))

// Ensure getDeclaredPropertyOfResolvingType works for inherited declared members

interface A { kind: string; m: this["kind"] }
>A : Symbol(A, Decl(circularDeclaredPropertyAccess.ts, 12, 52))
>kind : Symbol(A.kind, Decl(circularDeclaredPropertyAccess.ts, 16, 13))
>m : Symbol(A.m, Decl(circularDeclaredPropertyAccess.ts, 16, 27))

interface B { m: "a" }
>B : Symbol(B, Decl(circularDeclaredPropertyAccess.ts, 16, 45))
>m : Symbol(B.m, Decl(circularDeclaredPropertyAccess.ts, 17, 13))

type AB = A & B;
>AB : Symbol(AB, Decl(circularDeclaredPropertyAccess.ts, 17, 22))
>A : Symbol(A, Decl(circularDeclaredPropertyAccess.ts, 12, 52))
>B : Symbol(B, Decl(circularDeclaredPropertyAccess.ts, 16, 45))

interface D extends AB { kind: "a" }
>D : Symbol(D, Decl(circularDeclaredPropertyAccess.ts, 18, 16))
>AB : Symbol(AB, Decl(circularDeclaredPropertyAccess.ts, 17, 22))
>kind : Symbol(D.kind, Decl(circularDeclaredPropertyAccess.ts, 20, 24))

interface E extends D {}
>E : Symbol(E, Decl(circularDeclaredPropertyAccess.ts, 20, 36))
>D : Symbol(D, Decl(circularDeclaredPropertyAccess.ts, 18, 16))

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//// [tests/cases/compiler/circularDeclaredPropertyAccess.ts] ////

=== circularDeclaredPropertyAccess.ts ===
// https://github.com/microsoft/TypeScript/issues/64605

interface Internals<I = unknown> { input: I }
>input : I

interface Schema { _zod: Internals }
>_zod : Internals<unknown>

type RecordInput<V extends Schema> = V extends unknown ? Record<string, V["_zod"]["input"]> : never;
>RecordInput : RecordInput<V>

interface RecordSchema<V extends Schema> extends Schema { _zod: Internals<RecordInput<V>> }
>_zod : Internals<RecordInput<V>>

interface UnionInternals<T extends readonly Schema[]> extends Internals<T[number]["_zod"]["input"]> {}
interface UnionSchema<T extends readonly Schema[]> extends Schema { _zod: UnionInternals<T> }
>_zod : UnionInternals<T>

type JsonValue = { [k: string]: JsonValue };
>JsonValue : JsonValue
>k : string

type _Json = UnionSchema<[RecordSchema<Json>]>;
>_Json : _Json

type _JsonInternals = _Json["_zod"];
>_JsonInternals : UnionInternals<[RecordSchema<Json>]>

interface JsonInternals extends _JsonInternals { input: JsonValue }
>input : JsonValue

interface Json extends _Json { _zod: JsonInternals }
>_zod : JsonInternals

// Ensure getDeclaredPropertyOfResolvingType works for inherited declared members

interface A { kind: string; m: this["kind"] }
>kind : string
>m : this["kind"]

interface B { m: "a" }
>m : "a"

type AB = A & B;
>AB : AB

interface D extends AB { kind: "a" }
>kind : "a"

interface E extends D {}

Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
mutuallyRecursiveInference.ts(2,5): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
mutuallyRecursiveInference.ts(9,5): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
mutuallyRecursiveInference.ts(10,5): error TS2564: Property 'b' has no initializer and is not definitely assigned in the constructor.
mutuallyRecursiveInference.ts(12,9): error TS5114: Instantiations of type 'T' appear infinitely circular.


==== mutuallyRecursiveInference.ts (4 errors) ====
==== mutuallyRecursiveInference.ts (3 errors) ====
class T<A> {
a: A;
~
Expand All @@ -23,8 +22,6 @@ mutuallyRecursiveInference.ts(12,9): error TS5114: Instantiations of type 'T' ap
!!! error TS2564: Property 'b' has no initializer and is not definitely assigned in the constructor.
m2() {
this.a
~~~~~~
!!! error TS5114: Instantiations of type 'T' appear infinitely circular.
}
}

Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
recursiveIndexedAccessSimplification.ts(5,5): error TS4109: Type arguments for 'Array' circularly reference themselves.
recursiveIndexedAccessSimplification.ts(5,9): error TS2536: Type 'number' cannot be used to index type 'Recur<T>'.
recursiveIndexedAccessSimplification.ts(8,5): error TS2322: Type '[string, ...Recur<T>[]]' is not assignable to type 'Recur<T>'.
Type '[string, ...Recur<T>[]]' is not assignable to type 'Recur<T>[number][]'.
Type 'string | Recur<T>' is not assignable to type 'Recur<T>[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
Type 'string' is not assignable to type 'Recur<T>[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
Type 'string' is not assignable to type 'Recur<T>[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
Type 'string' is not assignable to type '(T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
recursiveIndexedAccessSimplification.ts(8,12): error TS2589: Type instantiation is excessively deep and possibly infinite.
recursiveIndexedAccessSimplification.ts(12,37): error TS2589: Type instantiation is excessively deep and possibly infinite.


==== recursiveIndexedAccessSimplification.ts (5 errors) ====
==== recursiveIndexedAccessSimplification.ts (2 errors) ====
// https://github.com/microsoft/TypeScript/issues/63270

type Recur<T> =
(T extends (unknown[]) ? {} : { [K in keyof T]?: Recur<T[K]>}) |
[...Recur<T>[number][]];
~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS4109: Type arguments for 'Array' circularly reference themselves.
~~~~~~~~~~~~~~~~
!!! error TS2536: Type 'number' cannot be used to index type 'Recur<T>'.

Expand All @@ -30,13 +25,9 @@ recursiveIndexedAccessSimplification.ts(12,37): error TS2589: Type instantiation
!!! error TS2322: Type 'string' is not assignable to type 'Recur<T>[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
!!! error TS2322: Type 'string' is not assignable to type 'Recur<T>[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
!!! error TS2322: Type 'string' is not assignable to type '(T extends unknown[] ? {} : { [K in keyof T]?: Recur<T[K]> | undefined; })[number]'.
~~~~~~~~~~~~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
}

function a<T>(l: Recur<T>[]): void {
const x: Recur<T> | undefined = join(l);
~~~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
}

Original file line number Diff line number Diff line change
@@ -1,34 +1,22 @@
sharedMemberLayoutThisIntersectionBase.ts(5,11): error TS2589: Type instantiation is excessively deep and possibly infinite.
sharedMemberLayoutThisIntersectionBase.ts(7,1): error TS2589: Type instantiation is excessively deep and possibly infinite.
sharedMemberLayoutThisIntersectionBase.ts(7,3): error TS2339: Property 'm' does not exist on type 'D<"b">'.
sharedMemberLayoutThisIntersectionBase.ts(10,7): error TS2589: Type instantiation is excessively deep and possibly infinite.
sharedMemberLayoutThisIntersectionBase.ts(12,1): error TS2589: Type instantiation is excessively deep and possibly infinite.
sharedMemberLayoutThisIntersectionBase.ts(12,3): error TS2339: Property 'm' does not exist on type 'C<"b">'.


==== sharedMemberLayoutThisIntersectionBase.ts (6 errors) ====
==== sharedMemberLayoutThisIntersectionBase.ts (2 errors) ====
interface A { kind: string; m: this["kind"] }
interface B { m: "a" }
type AB = A & B;

interface D<T extends string> extends AB { kind: T }
~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
declare let d: D<"b">;
d.m;
~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
~
!!! error TS2339: Property 'm' does not exist on type 'D<"b">'.

declare const ABCtor: new () => AB;
class C<T extends string> extends ABCtor { declare kind: T }
~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
declare let c: C<"b">;
c.m;
~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
~
!!! error TS2339: Property 'm' does not exist on type 'C<"b">'.

Expand Down
Loading
Loading