Skip to content

Commit edeeea3

Browse files
Read a type's declared members while its base types resolve
Fixes #64605.
1 parent fed0bf2 commit edeeea3

9 files changed

Lines changed: 422 additions & 4 deletions

‎tsc/internal/checker/checker.go‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,7 @@ type Checker struct {
596596
TotalInstantiationCount uint32
597597
instantiationCount uint32
598598
instantiationStack []*Type
599+
resolvingMembersStack []resolvingMembers
599600
conditionalConstraintDepth uint32
600601
inlineLevel int
601602
serializationLevel int
@@ -19307,6 +19308,11 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr
1930719308
t = c.getReducedApparentType(t)
1930819309
switch {
1930919310
case t.flags&TypeFlagsObject != 0:
19311+
if t.objectFlags&ObjectFlagsMembersResolved == 0 {
19312+
if symbol := c.getDeclaredPropertyOfResolvingType(t, name); symbol != nil {
19313+
return symbol
19314+
}
19315+
}
1931019316
resolved := c.resolveStructuredTypeMembers(t)
1931119317
symbol := resolved.members[name]
1931219318
if symbol != nil {
@@ -19537,6 +19543,7 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters
1953719543
members = maps.Clone(members)
1953819544
}
1953919545
thisArgument := core.LastOrNil(typeArguments)
19546+
c.resolvingMembersStack = append(c.resolvingMembersStack, resolvingMembers{t: t, declaredMembers: resolved.declaredMembers, members: members})
1954019547
for _, baseType := range baseTypes {
1954119548
instantiatedBaseType := baseType
1954219549
if thisArgument != nil {
@@ -19555,10 +19562,51 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters
1955519562
return findIndexInfo(indexInfos, info.keyType) == nil
1955619563
}))
1955719564
}
19565+
c.resolvingMembersStack = c.resolvingMembersStack[:len(c.resolvingMembersStack)-1]
1955819566
}
1955919567
c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos)
1956019568
}
1956119569

19570+
// Base type arguments may refer back to a type whose members are being resolved. Inherited members never
19571+
// replace a declared value member, so such a reference can read the declared properties without resolving
19572+
// the members again, which would repeat the same instantiations until the depth limit.
19573+
type resolvingMembers struct {
19574+
t *Type
19575+
declaredMembers ast.SymbolTable
19576+
members ast.SymbolTable
19577+
}
19578+
19579+
func (c *Checker) getResolvingMembers(t *Type) *resolvingMembers {
19580+
for i := len(c.resolvingMembersStack) - 1; i >= 0; i-- {
19581+
if c.resolvingMembersStack[i].t == t {
19582+
return &c.resolvingMembersStack[i]
19583+
}
19584+
}
19585+
return nil
19586+
}
19587+
19588+
// Return the property with the given name that t declares itself, if t is a type whose members are being resolved.
19589+
func (c *Checker) getDeclaredPropertyOfResolvingType(t *Type, name string) *ast.Symbol {
19590+
if r := c.getResolvingMembers(t); r != nil {
19591+
if symbol := r.declaredMembers[name]; symbol != nil && symbol.Flags&ast.SymbolFlagsValue != 0 {
19592+
return r.members[name]
19593+
}
19594+
}
19595+
return nil
19596+
}
19597+
19598+
// Return true if t is a type whose members are being resolved and t declares a property itself.
19599+
func (c *Checker) resolvingTypeDeclaresProperties(t *Type) bool {
19600+
if r := c.getResolvingMembers(t); r != nil {
19601+
for name, symbol := range r.declaredMembers {
19602+
if symbol.Flags&ast.SymbolFlagsValue != 0 && !isReservedMemberName(name) {
19603+
return true
19604+
}
19605+
}
19606+
}
19607+
return false
19608+
}
19609+
1956219610
func findIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexInfo {
1956319611
for _, info := range indexInfos {
1956419612
if info.keyType == keyType {
@@ -27897,6 +27945,9 @@ func (c *Checker) getPropertyNameFromIndex(indexType *Type, accessNode *ast.Node
2789727945
}
2789827946

2789927947
func (c *Checker) isStringIndexSignatureOnlyTypeWorker(t *Type) bool {
27948+
if t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsMembersResolved == 0 && c.resolvingTypeDeclaresProperties(t) {
27949+
return false
27950+
}
2790027951
return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && len(c.getPropertiesOfType(t)) == 0 && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil ||
2790127952
t.flags&TypeFlagsUnionOrIntersection != 0 && core.Every(t.Types(), c.isStringIndexSignatureOnlyType)
2790227953
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//// [tests/cases/compiler/circularBaseTypeArgumentsReadDeclaredMembers.ts] ////
2+
3+
=== circularBaseTypeArgumentsReadDeclaredMembers.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/64605
5+
6+
interface Internals<I = unknown> { input: I }
7+
>Internals : Symbol(Internals, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 0, 0))
8+
>I : Symbol(I, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 2, 20))
9+
>input : Symbol(Internals.input, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 2, 34))
10+
>I : Symbol(I, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 2, 20))
11+
12+
interface Schema { _zod: Internals }
13+
>Schema : Symbol(Schema, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 2, 45))
14+
>_zod : Symbol(Schema._zod, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 3, 18))
15+
>Internals : Symbol(Internals, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 0, 0))
16+
17+
type RecordInput<V extends Schema> = V extends unknown ? Record<string, V["_zod"]["input"]> : never;
18+
>RecordInput : Symbol(RecordInput, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 3, 36))
19+
>V : Symbol(V, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 4, 17))
20+
>Schema : Symbol(Schema, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 2, 45))
21+
>V : Symbol(V, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 4, 17))
22+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
23+
>V : Symbol(V, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 4, 17))
24+
25+
interface RecordSchema<V extends Schema> extends Schema { _zod: Internals<RecordInput<V>> }
26+
>RecordSchema : Symbol(RecordSchema, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 4, 100))
27+
>V : Symbol(V, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 5, 23))
28+
>Schema : Symbol(Schema, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 2, 45))
29+
>Schema : Symbol(Schema, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 2, 45))
30+
>_zod : Symbol(RecordSchema._zod, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 5, 57))
31+
>Internals : Symbol(Internals, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 0, 0))
32+
>RecordInput : Symbol(RecordInput, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 3, 36))
33+
>V : Symbol(V, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 5, 23))
34+
35+
interface UnionInternals<T extends readonly Schema[]> extends Internals<T[number]["_zod"]["input"]> {}
36+
>UnionInternals : Symbol(UnionInternals, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 5, 91))
37+
>T : Symbol(T, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 6, 25))
38+
>Schema : Symbol(Schema, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 2, 45))
39+
>Internals : Symbol(Internals, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 0, 0))
40+
>T : Symbol(T, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 6, 25))
41+
42+
interface UnionSchema<T extends readonly Schema[]> extends Schema { _zod: UnionInternals<T> }
43+
>UnionSchema : Symbol(UnionSchema, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 6, 102))
44+
>T : Symbol(T, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 7, 22))
45+
>Schema : Symbol(Schema, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 2, 45))
46+
>Schema : Symbol(Schema, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 2, 45))
47+
>_zod : Symbol(UnionSchema._zod, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 7, 67))
48+
>UnionInternals : Symbol(UnionInternals, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 5, 91))
49+
>T : Symbol(T, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 7, 22))
50+
51+
type JsonValue = { [k: string]: JsonValue };
52+
>JsonValue : Symbol(JsonValue, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 7, 93))
53+
>k : Symbol(k, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 8, 20))
54+
>JsonValue : Symbol(JsonValue, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 7, 93))
55+
56+
type _Json = UnionSchema<[RecordSchema<Json>]>;
57+
>_Json : Symbol(_Json, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 8, 44))
58+
>UnionSchema : Symbol(UnionSchema, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 6, 102))
59+
>RecordSchema : Symbol(RecordSchema, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 4, 100))
60+
>Json : Symbol(Json, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 11, 67))
61+
62+
type _JsonInternals = _Json["_zod"];
63+
>_JsonInternals : Symbol(_JsonInternals, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 9, 47))
64+
>_Json : Symbol(_Json, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 8, 44))
65+
66+
interface JsonInternals extends _JsonInternals { input: JsonValue }
67+
>JsonInternals : Symbol(JsonInternals, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 10, 36))
68+
>_JsonInternals : Symbol(_JsonInternals, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 9, 47))
69+
>input : Symbol(JsonInternals.input, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 11, 48))
70+
>JsonValue : Symbol(JsonValue, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 7, 93))
71+
72+
interface Json extends _Json { _zod: JsonInternals }
73+
>Json : Symbol(Json, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 11, 67))
74+
>_Json : Symbol(_Json, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 8, 44))
75+
>_zod : Symbol(Json._zod, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 12, 30))
76+
>JsonInternals : Symbol(JsonInternals, Decl(circularBaseTypeArgumentsReadDeclaredMembers.ts, 10, 36))
77+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//// [tests/cases/compiler/circularBaseTypeArgumentsReadDeclaredMembers.ts] ////
2+
3+
=== circularBaseTypeArgumentsReadDeclaredMembers.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/64605
5+
6+
interface Internals<I = unknown> { input: I }
7+
>input : I
8+
9+
interface Schema { _zod: Internals }
10+
>_zod : Internals<unknown>
11+
12+
type RecordInput<V extends Schema> = V extends unknown ? Record<string, V["_zod"]["input"]> : never;
13+
>RecordInput : RecordInput<V>
14+
15+
interface RecordSchema<V extends Schema> extends Schema { _zod: Internals<RecordInput<V>> }
16+
>_zod : Internals<RecordInput<V>>
17+
18+
interface UnionInternals<T extends readonly Schema[]> extends Internals<T[number]["_zod"]["input"]> {}
19+
interface UnionSchema<T extends readonly Schema[]> extends Schema { _zod: UnionInternals<T> }
20+
>_zod : UnionInternals<T>
21+
22+
type JsonValue = { [k: string]: JsonValue };
23+
>JsonValue : JsonValue
24+
>k : string
25+
26+
type _Json = UnionSchema<[RecordSchema<Json>]>;
27+
>_Json : _Json
28+
29+
type _JsonInternals = _Json["_zod"];
30+
>_JsonInternals : UnionInternals<[RecordSchema<Json>]>
31+
32+
interface JsonInternals extends _JsonInternals { input: JsonValue }
33+
>input : JsonValue
34+
35+
interface Json extends _Json { _zod: JsonInternals }
36+
>_zod : JsonInternals
37+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
circularBaseTypeArgumentsReadDeclaredMembers2.ts(21,14): error TS2322: Type '"required"' is not assignable to type '"optional"'.
2+
3+
4+
==== circularBaseTypeArgumentsReadDeclaredMembers2.ts (1 errors) ====
5+
// https://github.com/microsoft/TypeScript/issues/64605
6+
// The shape of Zod's z.json() types: RecordInternals declares optin, and its base type arguments read it.
7+
8+
interface Internals<O = unknown, I = unknown> { output: O; input: I; optin?: "optional" | undefined }
9+
interface Schema<Z extends Internals = Internals> { _zod: Z }
10+
type OptionalIn = { _zod: { optin: "optional" } };
11+
type RecordInput<V extends Schema> = [V] extends [OptionalIn] ? Partial<Record<string, V["_zod"]["input"]>> : Record<string, V["_zod"]["input"]>;
12+
interface RecordInternals<V extends Schema> extends Internals<unknown, RecordInput<V>> { optin?: "optional" | undefined }
13+
interface RecordSchema<V extends Schema> extends Schema<RecordInternals<V>> { _zod: RecordInternals<V> }
14+
type IsOptionalIn<T extends Schema> = T extends OptionalIn ? true : false;
15+
interface UnionInternals<T extends readonly Schema[]> extends Internals {
16+
optin: IsOptionalIn<T[number]> extends false ? "optional" | undefined : "optional";
17+
}
18+
interface UnionSchema<T extends readonly Schema[]> extends Schema<UnionInternals<T>> { _zod: UnionInternals<T> }
19+
type _Json = UnionSchema<[RecordSchema<Json>]>;
20+
interface JsonInternals extends UnionInternals<[RecordSchema<Json>]> { input: unknown }
21+
interface Json extends _Json { _zod: JsonInternals }
22+
23+
export const r: RecordInput<Json> = { a: 1 };
24+
export const o: Json["_zod"]["optin"] = undefined;
25+
export const bad: Json["_zod"]["optin"] = "required"; // error
26+
~~~
27+
!!! error TS2322: Type '"required"' is not assignable to type '"optional"'.
28+

0 commit comments

Comments
 (0)