From fd431c68e52817f708a222ff6ba24a17247503be Mon Sep 17 00:00:00 2001 From: Colin McDonnell <3084745+colinhacks@users.noreply.github.com> Date: Thu, 27 Aug 2026 10:01:21 -0700 Subject: [PATCH 1/9] Prove a member cannot answer yet, instead of predicting it Inferring a type argument verifies the candidate against its constraint. That check reports nothing and only decides whether to keep the candidate, but it walks every source property, and for an un-annotated getter that means inferring from its body -- which re-enters the declaration being resolved. Mark the resolution stack when the constraint check opens. A cycle that reaches below the mark was caused by the question rather than by the program, so the attempt is abandoned at the point that forced the member and the member is reported as unanswerable. Nothing computed from the circular value ever completes, so there is no diagnostic to suppress, no placeholder to hand out, no cache write to journal and nothing to retract. Two things the unwind exposes. A comparison that passed over an unanswerable member may keep the candidate but must not reject one, and its success is not written to the global relation cache. And resolveObjectTypeMembers publishes a partial member table as its own recursion guard: abandoning inside that window would leave the type marked resolved while holding only its self-declared members, permanently, for every later reader -- so the flags are cleared on the way out. --- tsc/internal/checker/checker.go | 237 ++++++++++++++++++++++++++++++ tsc/internal/checker/inference.go | 10 +- tsc/internal/checker/relater.go | 26 +++- 3 files changed, 269 insertions(+), 4 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index ca77e97af8547..ce8771e604e8f 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -794,6 +794,11 @@ type Checker struct { typeofType *Type typeResolutions []TypeResolution resolutionStart int + provisionalDepth int + provisionalMark int + unresolvableMembers int + skippedMemberChecks []skippedMemberCheck + recheckingSkippedMembers bool varianceStack []VarianceStackEntry apparentArgumentCount *int lastGetCombinedNodeFlagsNode *ast.Node @@ -2538,6 +2543,57 @@ func (c *Checker) checkDeferredNodes(context *ast.SourceFile) { c.checkDeferredNode(node) } links.deferredNodes = collections.OrderedSet[*ast.Node]{} + c.recheckSkippedMembers(context) +} + +// A comparison that passed over a member did not verify the constraint on it, it postponed it. This is +// where the postponement is honoured: by now the declarations involved have types of their own, so the +// member is compared against the same target, this time reporting. Without it a getter whose type does +// not satisfy the constraint is simply accepted. +func (c *Checker) recheckSkippedMembers(context *ast.SourceFile) { + pending := c.skippedMemberChecks + c.skippedMemberChecks = nil + c.recheckingSkippedMembers = true + defer func() { c.recheckingSkippedMembers = false }() + for _, check := range pending { + // An obligation is honoured in the pass for the file its error belongs to, so the diagnostic lands + // with that file. Where that file has been checked already there is no later pass to wait for. + file := ast.GetSourceFileOfNode(check.errorNode) + if file != context && !c.sourceFileLinks.Get(file).typeChecked { + c.skippedMemberChecks = append(c.skippedMemberChecks, check) + continue + } + c.checkTypeRelatedTo(c.getNonMissingTypeOfSymbol(check.property), check.target, check.relation, check.errorNode) + } +} + +// Record a member the constraint check could not resolve, so it can be compared once it has a type. +// Dropping it instead would leave nothing recording that the comparison never happened, which is how an +// invalid member gets in. +func (c *Checker) postponeMemberCheck(prop *ast.Symbol, target *Type, relation *Relation) { + if c.recheckingSkippedMembers { + return + } + // The accessor's own declaration, not the relater's error node: that one is whatever the outermost + // comparison was handed, so taking it would report one defect at different places depending on which + // sub-comparison happened to skip the member. + var where *ast.Node + for _, declaration := range prop.Declarations { + if declaration == nil { + continue + } + if name := declaration.Name(); name != nil { + where = name + } else { + where = declaration + } + if ast.IsGetAccessorDeclaration(declaration) { + break + } + } + if where != nil { + c.skippedMemberChecks = append(c.skippedMemberChecks, skippedMemberCheck{property: prop, target: target, relation: relation, errorNode: where}) + } } func (c *Checker) checkDeferredNode(node *ast.Node) { @@ -19059,6 +19115,170 @@ func (c *Checker) getCombinedModifierFlagsCached(node *ast.Node) ast.ModifierFla return c.lastGetCombinedModifierFlagsResult } +// Whether any base of t could still contribute a member named name. resolveObjectTypeMembers publishes +// the self-declared table before it walks the bases, so a miss inside that window is "not known yet" +// rather than "absent" -- but only for a name a base actually declares. Reading declaration tables +// answers that without forcing a single type. +func (c *Checker) mayInheritProperty(t *Type, name string, seen []*Type) bool { + declared := t + if declared.objectFlags&ObjectFlagsReference != 0 { + if target := declared.Target(); target != nil { + declared = target + } + } + if declared.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsTuple) == 0 { + return true + } + if slices.Contains(seen, declared) { + return false + } + seen = append(seen, declared) + for _, base := range c.getBaseTypes(declared) { + baseDeclared := base + if baseDeclared.objectFlags&ObjectFlagsReference != 0 { + if target := baseDeclared.Target(); target != nil { + baseDeclared = target + } + } + if baseDeclared.symbol != nil && baseDeclared.symbol.Members[name] != nil { + return true + } + if c.mayInheritProperty(base, name, seen) { + return true + } + } + return false +} + +// A constraint comparison postponed because the member could not be worked out yet. +type skippedMemberCheck struct { + property *ast.Symbol + target *Type + relation *Relation + errorNode *ast.Node +} + +// Raised when a resolution started by a provisional question re-enters a declaration that was already +// being resolved when that question was asked. Nothing computed from the circular value is allowed to +// complete, so no diagnostic is reported and no cache records it. +type circularQuestion struct{} + +// The checker's push/pop state, so an abandoned attempt can be unwound without leaving a push behind. +type checkerStacks struct { + typeResolutions, varianceStack, activeMappers, activeTypeMappersCaches int + antecedentTypes, awaitedTypeStack, contextualBindingPatterns int + contextualInfos, flowLoopStack, inferenceContextInfos, sharedFlows int + reverseMappedSourceStack, reverseMappedTargetStack int + renamedBindingElementsInTypes, resolutionStart int + instantiationDepth, conditionalConstraintDepth uint32 + currentNode *ast.Node + varianceTypeParameter *Type + flowTypeCache map[*ast.Node]*Type + reliabilityFlags RelationComparisonResult + reverseExpandingFlags ExpandingFlags + flowAnalysisDisabled, withinUnreachableCode bool +} + +func (c *Checker) saveStacks() checkerStacks { + return checkerStacks{ + typeResolutions: len(c.typeResolutions), varianceStack: len(c.varianceStack), + activeMappers: len(c.activeMappers), activeTypeMappersCaches: len(c.activeTypeMappersCaches), + antecedentTypes: len(c.antecedentTypes), awaitedTypeStack: len(c.awaitedTypeStack), + contextualBindingPatterns: len(c.contextualBindingPatterns), contextualInfos: len(c.contextualInfos), + flowLoopStack: len(c.flowLoopStack), inferenceContextInfos: len(c.inferenceContextInfos), + sharedFlows: len(c.sharedFlows), reverseMappedSourceStack: len(c.reverseMappedSourceStack), + reverseMappedTargetStack: len(c.reverseMappedTargetStack), + renamedBindingElementsInTypes: len(c.renamedBindingElementsInTypes), + resolutionStart: c.resolutionStart, instantiationDepth: c.instantiationDepth, + conditionalConstraintDepth: c.conditionalConstraintDepth, currentNode: c.currentNode, + varianceTypeParameter: c.varianceTypeParameter, flowTypeCache: c.flowTypeCache, + reliabilityFlags: c.reliabilityFlags, reverseExpandingFlags: c.reverseExpandingFlags, + flowAnalysisDisabled: c.flowAnalysisDisabled, withinUnreachableCode: c.withinUnreachableCode, + } +} + +func (c *Checker) restoreStacks(s checkerStacks) { + clear(c.typeResolutions[s.typeResolutions:]) + c.typeResolutions = c.typeResolutions[:s.typeResolutions] + c.varianceStack = c.varianceStack[:s.varianceStack] + clear(c.activeMappers[s.activeMappers:]) + c.activeMappers = c.activeMappers[:s.activeMappers] + for _, cache := range c.activeTypeMappersCaches[s.activeTypeMappersCaches:] { + clear(cache) + } + c.activeTypeMappersCaches = c.activeTypeMappersCaches[:s.activeTypeMappersCaches] + c.antecedentTypes = c.antecedentTypes[:s.antecedentTypes] + c.awaitedTypeStack = c.awaitedTypeStack[:s.awaitedTypeStack] + c.contextualBindingPatterns = c.contextualBindingPatterns[:s.contextualBindingPatterns] + c.contextualInfos = c.contextualInfos[:s.contextualInfos] + c.flowLoopStack = c.flowLoopStack[:s.flowLoopStack] + c.inferenceContextInfos = c.inferenceContextInfos[:s.inferenceContextInfos] + c.sharedFlows = c.sharedFlows[:s.sharedFlows] + c.reverseMappedSourceStack = c.reverseMappedSourceStack[:s.reverseMappedSourceStack] + c.reverseMappedTargetStack = c.reverseMappedTargetStack[:s.reverseMappedTargetStack] + c.renamedBindingElementsInTypes = c.renamedBindingElementsInTypes[:s.renamedBindingElementsInTypes] + c.resolutionStart = s.resolutionStart + c.instantiationDepth = s.instantiationDepth + c.conditionalConstraintDepth = s.conditionalConstraintDepth + c.currentNode = s.currentNode + c.varianceTypeParameter = s.varianceTypeParameter + c.flowTypeCache = s.flowTypeCache + c.reliabilityFlags = s.reliabilityFlags + c.reverseExpandingFlags = s.reverseExpandingFlags + c.flowAnalysisDisabled = s.flowAnalysisDisabled + c.withinUnreachableCode = s.withinUnreachableCode +} + +// Run a comparison that reports nothing and only decides whether to keep an inferred candidate. Inside +// it a member whose type cannot be computed yet is passed over rather than forced -- see +// tryGetTypeOfMember -- and unresolvableMembers counts how many were. +func (c *Checker) compareProvisionally(compare func() Ternary) (result Ternary, answeredInFull bool) { + savedMark, savedDepth := c.provisionalMark, c.provisionalDepth + stacks := c.saveStacks() + skippedBefore := c.unresolvableMembers + c.provisionalMark = len(c.typeResolutions) + c.provisionalDepth++ + defer func() { + c.provisionalDepth, c.provisionalMark = savedDepth, savedMark + if r := recover(); r != nil { + // A member forced somewhere other than tryGetTypeOfMember, so nothing nearer caught it. The + // comparison is over either way, and it answered for less than the whole type. + if _, ours := r.(circularQuestion); !ours { + panic(r) + } + c.restoreStacks(stacks) + c.unresolvableMembers++ + result, answeredInFull = TernaryTrue, false + } + }() + result = compare() + return result, c.unresolvableMembers == skippedBefore +} + +// Get a member's type for a provisional comparison, or report that it has none to give yet. A getter is +// how a self-referential object literal is written and the one member kind whose type is computed +// lazily, so forcing one here routinely re-enters the declaration the comparison is being made for. The +// attempt is made rather than predicted -- a member is only passed over once it has proved unanswerable +// -- and abandoning it leaves nothing behind, because nothing computed from the circular value +// completes. +func (c *Checker) tryGetTypeOfMember(get func(*ast.Symbol) *Type, symbol *ast.Symbol) (t *Type, resolved bool) { + if c.provisionalDepth == 0 { + return get(symbol), true + } + stacks := c.saveStacks() + defer func() { + if r := recover(); r != nil { + if _, ours := r.(circularQuestion); !ours { + panic(r) + } + c.restoreStacks(stacks) + c.unresolvableMembers++ + t, resolved = nil, false + } + }() + return get(symbol), true +} + /** * Push an entry on the type resolution stack. If an entry with the given target and the given property name * is already on the stack, and no entries in between already have a type, then a circularity has occurred. @@ -19073,6 +19293,12 @@ func (c *Checker) getCombinedModifierFlagsCached(node *ast.Node) ast.ModifierFla func (c *Checker) pushTypeResolution(target TypeSystemEntity, propertyName TypeSystemPropertyName) bool { resolutionCycleStartIndex := c.findResolutionCycleStartIndex(target, propertyName) if resolutionCycleStartIndex >= 0 { + if c.provisionalDepth != 0 && resolutionCycleStartIndex < c.provisionalMark { + // The cycle runs back through a declaration that was already being resolved when the + // provisional question was asked, so the question caused it. Abandon the attempt instead + // of answering `any`: a verdict that decides nothing must not fix a declaration's type. + panic(circularQuestion{}) + } // A cycle was found for i := resolutionCycleStartIndex; i < len(c.typeResolutions); i++ { c.typeResolutions[i].result = false @@ -19376,6 +19602,17 @@ func (c *Checker) isApplicableIndexType(source *Type, target *Type) bool { func (c *Checker) resolveStructuredTypeMembers(t *Type) *StructuredType { if t.objectFlags&ObjectFlagsMembersResolved == 0 { + if c.provisionalDepth != 0 { + // resolveObjectTypeMembers publishes a partial table as its own recursion guard and completes + // it afterwards, so an attempt abandoned in that window would leave a type holding only what it + // declares itself, marked resolved. Put it back to unresolved so the next reader recomputes it. + defer func() { + if r := recover(); r != nil { + t.objectFlags &^= ObjectFlagsMembersResolved | ObjectFlagsUnresolvedMembers + panic(r) + } + }() + } switch { case t.flags&TypeFlagsObject != 0: switch { diff --git a/tsc/internal/checker/inference.go b/tsc/internal/checker/inference.go index 4bb9d2923d9ab..c5c1fb6ae1610 100644 --- a/tsc/internal/checker/inference.go +++ b/tsc/internal/checker/inference.go @@ -1380,7 +1380,15 @@ func (c *Checker) getInferredType(n *InferenceContext, index int) *Type { instantiatedConstraint := c.instantiateType(constraint, n.nonFixingMapper) if inferredType != nil { constraintWithThis := c.getTypeWithThisArgument(instantiatedConstraint, inferredType, false) - if n.compareTypes(inferredType, constraintWithThis, false) == TernaryFalse { + // Verifying a candidate against its constraint reports nothing and only decides whether to keep + // the candidate, so it must not force a member whose type is still being computed: an object + // literal argument routinely names the very declaration being resolved. A comparison that passed + // over such a member answered for less than the whole type, so it may keep the candidate but + // never reject it. + comparison, answeredInFull := c.compareProvisionally(func() Ternary { + return n.compareTypes(inferredType, constraintWithThis, false) + }) + if comparison == TernaryFalse && answeredInFull { var filteredByConstraint *Type if inference.priority == InferencePriorityReturnType { // If we have a pure return type inference, we may succeed by removing constituents of the inferred type diff --git a/tsc/internal/checker/relater.go b/tsc/internal/checker/relater.go index 9008420fb7cd1..64b08231ecf37 100644 --- a/tsc/internal/checker/relater.go +++ b/tsc/internal/checker/relater.go @@ -3137,6 +3137,7 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError return TernaryMaybe } maybeStart := len(r.maybeKeys) + skipsBefore := r.c.unresolvableMembers r.maybeKeys = append(r.maybeKeys, id) r.maybeKeysSet.Add(id) saveExpandingFlags := r.expandingFlags @@ -3177,9 +3178,11 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError r.expandingFlags = saveExpandingFlags if result != TernaryFalse { if result == TernaryTrue || (len(r.sourceStack) == 0 && len(r.targetStack) == 0) { - if result == TernaryTrue || result == TernaryMaybe { + if (result == TernaryTrue || result == TernaryMaybe) && r.c.unresolvableMembers == skipsBefore { // If result is definitely true, record all maybe keys as having succeeded. Also, record Ternary.Maybe // results as having succeeded once we reach depth 0, but never record Ternary.Unknown results. + // A comparison that passed over a member it could not resolve answered for less than the whole + // type, and the relation cache is global, so the next question must ask again rather than read it. r.resetMaybeStack(maybeStart, propagatingVarianceFlags, true) } else { r.resetMaybeStack(maybeStart, propagatingVarianceFlags, false) @@ -4263,6 +4266,15 @@ func (r *Relater) propertiesRelatedTo(source *Type, target *Type, reportErrors b } requireOptionalProperties := (r.relation == r.c.subtypeRelation || r.relation == r.c.strictSubtypeRelation) && !isObjectLiteralType(source) && !r.c.isEmptyArrayLiteralType(source) && !isTupleType(source) unmatchedProperty := r.c.getUnmatchedProperty(source, target, requireOptionalProperties, false /*matchDiscriminantProperties*/) + // A miss while the source's member table is mid-assembly means "not known yet", not "absent" -- but + // only inside a provisional region, where the verdict is kept rather than reported, and only for a + // name some base declares. Suppressing it during an ordinary check makes a genuinely absent property + // look present, which is enough to send a conditional type down the wrong branch. + if unmatchedProperty != nil && r.c.provisionalDepth != 0 && source.objectFlags&ObjectFlagsUnresolvedMembers != 0 && + r.c.mayInheritProperty(source, unmatchedProperty.Name, nil) { + unmatchedProperty = nil + r.c.unresolvableMembers++ + } if unmatchedProperty != nil { if reportErrors && r.c.shouldReportUnmatchedPropertyError(source, target) { r.reportUnmatchedProperty(source, target, unmatchedProperty, requireOptionalProperties) @@ -4370,7 +4382,11 @@ func (r *Relater) isPropertySymbolTypeRelated(sourceProp *ast.Symbol, targetProp if effectiveTarget.flags&core.IfElse(r.relation == r.c.strictSubtypeRelation, TypeFlagsAny, TypeFlagsAnyOrUnknown) != 0 { return TernaryTrue } - effectiveSource := getTypeOfSourceProperty(sourceProp) + effectiveSource, resolved := r.c.tryGetTypeOfMember(getTypeOfSourceProperty, sourceProp) + if !resolved { + r.c.postponeMemberCheck(sourceProp, effectiveTarget, r.relation) + return TernaryTrue + } return r.isRelatedToEx(effectiveSource, effectiveTarget, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) } @@ -4678,7 +4694,11 @@ func (r *Relater) membersRelatedToIndexInfo(source *Type, targetInfo *IndexInfo, continue } if r.c.isApplicableIndexType(r.c.getLiteralTypeFromProperty(prop, TypeFlagsStringOrNumberLiteralOrUnique, false), keyType) { - propType := r.c.getNonMissingTypeOfSymbol(prop) + propType, resolved := r.c.tryGetTypeOfMember(r.c.getNonMissingTypeOfSymbol, prop) + if !resolved { + r.c.postponeMemberCheck(prop, targetInfo.valueType, r.relation) + continue + } var t *Type if r.c.exactOptionalPropertyTypes || propType.flags&TypeFlagsUndefined != 0 || keyType == r.c.numberType || prop.Flags&ast.SymbolFlagsOptional == 0 { t = propType From 11af8f5aab7e0cecea1f7b5da4d6aa1a90f12215 Mon Sep 17 00:00:00 2001 From: Colin McDonnell <3084745+colinhacks@users.noreply.github.com> Date: Thu, 27 Aug 2026 10:07:25 -0700 Subject: [PATCH 2/9] Add the conformance and editor coverage for this change Twelve compiler cases and five fourslash cases, carried over from the earlier implementation of this fix so the two are held to the same evidence. One baseline differs from that implementation and the difference is deliberate. A postponed constraint is now reported at the member that violates it -- `TS2741: Property 'out' is missing` -- rather than at the enclosing shape through a three-level assignability chain. All three file orderings agree, so the order-independence the fourslash cases exist to pin is unaffected. --- ...overThenDiagnosticsRecursiveGetter_test.go | 82 +++++ ...nstraintDiagnosticsRecursiveGetter_test.go | 67 +++++ ...siveTypeThroughObjectLiteralGetter.symbols | 175 +++++++++++ ...ursiveTypeThroughObjectLiteralGetter.types | 124 ++++++++ ...eralGetterAbsentDuringInference.errors.txt | 38 +++ ...LiteralGetterAbsentDuringInference.symbols | 83 ++++++ ...ctLiteralGetterAbsentDuringInference.types | 66 ++++ ...jectLiteralGetterAbsentProperty.errors.txt | 27 ++ ...hObjectLiteralGetterAbsentProperty.symbols | 50 ++++ ...ughObjectLiteralGetterAbsentProperty.types | 38 +++ ...ghObjectLiteralGetterConstraint.errors.txt | 27 ++ ...roughObjectLiteralGetterConstraint.symbols | 58 ++++ ...ThroughObjectLiteralGetterConstraint.types | 45 +++ ...ughObjectLiteralGetterCrossFile.errors.txt | 24 ++ ...hroughObjectLiteralGetterCrossFile.symbols | 53 ++++ ...eThroughObjectLiteralGetterCrossFile.types | 41 +++ ...roughObjectLiteralGetterDeclarationEmit.js | 79 +++++ ...ObjectLiteralGetterDeclarationEmit.symbols | 133 +++++++++ ...ghObjectLiteralGetterDeclarationEmit.types | 74 +++++ ...ectLiteralGetterInheritedMember.errors.txt | 77 +++++ ...ObjectLiteralGetterInheritedMember.symbols | 193 ++++++++++++ ...ghObjectLiteralGetterInheritedMember.types | 144 +++++++++ ...hroughObjectLiteralGetterOverloads.symbols | 104 +++++++ ...eThroughObjectLiteralGetterOverloads.types | 120 ++++++++ ...iteralGetterPostponedConstraint.errors.txt | 24 ++ ...ctLiteralGetterPostponedConstraint.symbols | 52 ++++ ...jectLiteralGetterPostponedConstraint.types | 40 +++ ...ypeThroughObjectLiteralGetterUnion.symbols | 179 +++++++++++ ...eTypeThroughObjectLiteralGetterUnion.types | 121 ++++++++ ...ghObjectLiteralGetterUnionOptional.symbols | 191 ++++++++++++ ...oughObjectLiteralGetterUnionOptional.types | 115 +++++++ ...roughObjectLiteralGetterVariations.symbols | 269 +++++++++++++++++ ...ThroughObjectLiteralGetterVariations.types | 281 ++++++++++++++++++ ...onstraintDiagnosticsConsumerFirst.baseline | 23 ++ ...ConstraintDiagnosticsConsumerOnly.baseline | 23 ++ ...dConstraintDiagnosticsSchemaFirst.baseline | 23 ++ ...recursiveTypeThroughObjectLiteralGetter.ts | 55 ++++ ...bjectLiteralGetterAbsentDuringInference.ts | 34 +++ ...hroughObjectLiteralGetterAbsentProperty.ts | 23 ++ ...ypeThroughObjectLiteralGetterConstraint.ts | 22 ++ ...TypeThroughObjectLiteralGetterCrossFile.ts | 27 ++ ...roughObjectLiteralGetterDeclarationEmit.ts | 43 +++ ...roughObjectLiteralGetterInheritedMember.ts | 73 +++++ ...TypeThroughObjectLiteralGetterOverloads.ts | 52 ++++ ...hObjectLiteralGetterPostponedConstraint.ts | 19 ++ ...siveTypeThroughObjectLiteralGetterUnion.ts | 56 ++++ ...ThroughObjectLiteralGetterUnionOptional.ts | 54 ++++ ...ypeThroughObjectLiteralGetterVariations.ts | 76 +++++ 48 files changed, 3797 insertions(+) create mode 100644 tsc/internal/fourslash/tests/hoverThenDiagnosticsRecursiveGetter_test.go create mode 100644 tsc/internal/fourslash/tests/postponedConstraintDiagnosticsRecursiveGetter_test.go create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.types create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.types create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.types create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.types create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.types create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.js create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.types create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.types create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.types create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.types create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.types create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.types create mode 100644 tsc/testdata/baselines/reference/fourslash/syntaxandSemanticDiagnostics/postponedConstraintDiagnosticsConsumerFirst.baseline create mode 100644 tsc/testdata/baselines/reference/fourslash/syntaxandSemanticDiagnostics/postponedConstraintDiagnosticsConsumerOnly.baseline create mode 100644 tsc/testdata/baselines/reference/fourslash/syntaxandSemanticDiagnostics/postponedConstraintDiagnosticsSchemaFirst.baseline create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetter.ts create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.ts create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.ts create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.ts create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnion.ts create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.ts create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterVariations.ts diff --git a/tsc/internal/fourslash/tests/hoverThenDiagnosticsRecursiveGetter_test.go b/tsc/internal/fourslash/tests/hoverThenDiagnosticsRecursiveGetter_test.go new file mode 100644 index 0000000000000..259d91d817f50 --- /dev/null +++ b/tsc/internal/fourslash/tests/hoverThenDiagnosticsRecursiveGetter_test.go @@ -0,0 +1,82 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/fourslash" + "github.com/microsoft/TypeScript/tsc/internal/testutil" +) + +// Regression test for https://github.com/microsoft/TypeScript/issues/62181 +// +// A getter naming the declaration being resolved used to report as `any`, and asking for that hover +// before pulling diagnostics changed how many errors came back, because the hover fixed the getter at +// `any` for everything that followed. The hover has to report the recursive type it actually has, and +// the diagnostics have to be the same whether or not anything asked for it first. +func TestHoverThenDiagnosticsRecursiveGetter(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, recursiveGetterContent) + defer done() + f.VerifyQuickInfoAt(t, "1", `(accessor) parent: ZodOptional>; +}>>`, "") + f.VerifyNoErrors(t) +} + +// The same file with nothing asked of it first, so the two can be compared. +func TestDiagnosticsOnlyRecursiveGetter(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, recursiveGetterContent) + defer done() + f.VerifyNoErrors(t) +} + +const recursiveGetterContent = `// @Filename: /tsconfig.json +{ "compilerOptions": { "strict": true, "target": "esnext" } } +// @Filename: /file.ts +interface ZodType { + optional: "true" | "false"; + output: T; +} + +interface ZodString extends ZodType { + optional: "false"; +} + +type ZodShape = Record; +type Prettify = { [K in keyof T]: T[K] } & {}; +type InferObjectType = Prettify< + { + [k in keyof Shape as Shape[k] extends { optional: "true" } + ? k + : never]?: Shape[k]["output"]; + } & { + [k in keyof Shape as Shape[k] extends { optional: "true" } + ? never + : k]: Shape[k]["output"]; + } +>; +interface ZodObject extends ZodType> { + optional: "false"; +} + +interface ZodOptional> + extends ZodType { + optional: "true"; +} + +declare function object(shape: T): ZodObject; +declare function string(): ZodString; +declare function optional>(schema: T): ZodOptional; + +const Category = object({ + name: string(), + get parent/*1*/() { + return optional(Category); + }, +}); + +export const output = Category.output;` diff --git a/tsc/internal/fourslash/tests/postponedConstraintDiagnosticsRecursiveGetter_test.go b/tsc/internal/fourslash/tests/postponedConstraintDiagnosticsRecursiveGetter_test.go new file mode 100644 index 0000000000000..8db4cdfddc2c4 --- /dev/null +++ b/tsc/internal/fourslash/tests/postponedConstraintDiagnosticsRecursiveGetter_test.go @@ -0,0 +1,67 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/fourslash" + "github.com/microsoft/TypeScript/tsc/internal/testutil" +) + +// A constraint that could not be answered while the getter was still being resolved is postponed and +// made again when a file's deferred work runs. In a batch compile every file is checked, so the queue +// always drains. The language service checks only what someone asks about, so the risk is that the +// answer depends on which file that was -- which is the complaint in +// https://github.com/microsoft/TypeScript/issues/62181 one layer down. +// +// `bad` holds an array of schemas, which is not a Schema, so it violates the constraint on S. The +// error belongs to schema.ts. These three pin that it is reported there in every order, including +// when the file carrying it is never the one asked about first. + +func TestPostponedConstraintDiagnosticsSchemaFirst(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, postponedConstraintContent) + defer done() + f.GoToFile(t, "/schema.ts") + f.GoToFile(t, "/consumer.ts") + f.VerifyBaselineNonSuggestionDiagnostics(t) +} + +func TestPostponedConstraintDiagnosticsConsumerFirst(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, postponedConstraintContent) + defer done() + f.GoToFile(t, "/consumer.ts") + f.GoToFile(t, "/schema.ts") + f.VerifyBaselineNonSuggestionDiagnostics(t) +} + +// The file the error belongs to is never opened. +func TestPostponedConstraintDiagnosticsConsumerOnly(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, postponedConstraintContent) + defer done() + f.GoToFile(t, "/consumer.ts") + f.VerifyBaselineNonSuggestionDiagnostics(t) +} + +const postponedConstraintContent = `// @Filename: /tsconfig.json +{ "compilerOptions": { "strict": true, "target": "esnext", "module": "nodenext", "moduleResolution": "nodenext" } } +// @Filename: /schema.ts +export interface Schema { + readonly out: O; +} +export type Shape = Record>; +export declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; + +export const tree = object({ + get bad() { + return [tree]; + }, +}); +// @Filename: /consumer.ts +import { tree } from "./schema.js"; + +export const out = tree.out;` diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.symbols new file mode 100644 index 0000000000000..a7ff11eba5926 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.symbols @@ -0,0 +1,175 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetter.ts] //// + +=== recursiveTypeThroughObjectLiteralGetter.ts === +// A getter is how a self-referential object literal is written, and its type is worked out lazily. +// Inferring the type argument for `object` must not force that getter, which reports a circularity +// and fixes the member at `any`. Reduced from #62180 and #62181. + +interface Internals { +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 0, 0)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 4, 20)) + + readonly out: O; +>out : Symbol(Internals.out, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 4, 38)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 4, 20)) +} +interface Schema { +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 6, 1)) + + readonly internals: Internals; +>internals : Symbol(Schema.internals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 7, 18)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 0, 0)) +} +type Out = T["internals"]["out"]; +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 9, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 10, 9)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 6, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 10, 9)) + +interface ArrayInternals extends Internals[]> {} +>ArrayInternals : Symbol(ArrayInternals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 10, 51)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 12, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 6, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 0, 0)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 9, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 12, 25)) + +interface ArraySchema extends Schema { +>ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 12, 73)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 13, 22)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 6, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 6, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 6, 1)) + + readonly internals: ArrayInternals; +>internals : Symbol(ArraySchema.internals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 13, 65)) +>ArrayInternals : Symbol(ArrayInternals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 10, 51)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 13, 22)) +} +interface StringInternals extends Internals {} +>StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 15, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 0, 0)) + +interface StringSchema extends Schema { +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 16, 54)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 6, 1)) + + readonly internals: StringInternals; +>internals : Symbol(StringSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 17, 39)) +>StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 15, 1)) +} + +type Shape = Readonly<{ [k: string]: Schema }>; +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 19, 1)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 21, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 6, 1)) + +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +>ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 21, 47)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 22, 26)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 19, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 0, 0)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 22, 64)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 22, 26)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 9, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 22, 26)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 22, 64)) + +interface ObjectSchema extends Schema { +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 22, 94)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 23, 23)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 19, 1)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 19, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 6, 1)) + + readonly internals: ObjectInternals; +>internals : Symbol(ObjectSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 23, 64)) +>ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 21, 47)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 23, 23)) +} + +declare function object(shape: S): ObjectSchema; +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 25, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 27, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 19, 1)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 27, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 27, 24)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 22, 94)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 27, 24)) + +declare function array(element: T): ArraySchema; +>array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 27, 68)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 28, 23)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 6, 1)) +>element : Symbol(element, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 28, 41)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 28, 23)) +>ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 12, 73)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 28, 23)) + +declare function text(): StringSchema; +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 28, 69)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 16, 54)) + +const node = object({ +>node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 31, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 25, 1)) + + name: text(), +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 31, 21)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 28, 69)) + + get children() { +>children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 32, 17)) + + return array(node); +>array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 27, 68)) +>node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 31, 5)) + + }, +}); + +type TreeNode = Out; +>TreeNode : Symbol(TreeNode, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 36, 3)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 9, 1)) +>node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 31, 5)) + +declare const sample: TreeNode; +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 39, 13)) +>TreeNode : Symbol(TreeNode, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 36, 3)) + +// The recursive member is an array of the same node type, not `any` and not `unknown`. +const leafName: string = sample.name; +>leafName : Symbol(leafName, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 42, 5)) +>sample.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 31, 21)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 39, 13)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 31, 21)) + +const nestedName: string = sample.children[0].children[0].name; +>nestedName : Symbol(nestedName, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 43, 5)) +>sample.children[0].children[0].name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 31, 21)) +>sample.children[0].children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 32, 17)) +>sample.children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 32, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 39, 13)) +>children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 32, 17)) +>children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 32, 17)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 31, 21)) + +// Those two pass on their own if the whole thing collapsed to `any`, which is exactly the failure +// this test exists to catch, so the recursion point is also probed where `any` cannot hide: an +// unknown key has to be an error, and a known one has to have the type it is declared with. +// @ts-expect-error +sample.children[0].missing; +>sample.children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 32, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 39, 13)) +>children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 32, 17)) + +// @ts-expect-error +const wrongLeafType: number = sample.children[0].name; +>wrongLeafType : Symbol(wrongLeafType, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 51, 5)) +>sample.children[0].name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 31, 21)) +>sample.children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 32, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 39, 13)) +>children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 32, 17)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 31, 21)) + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.types new file mode 100644 index 0000000000000..c8b4921b1cbc9 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.types @@ -0,0 +1,124 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetter.ts] //// + +=== recursiveTypeThroughObjectLiteralGetter.ts === +// A getter is how a self-referential object literal is written, and its type is worked out lazily. +// Inferring the type argument for `object` must not force that getter, which reports a circularity +// and fixes the member at `any`. Reduced from #62180 and #62181. + +interface Internals { + readonly out: O; +>out : O +} +interface Schema { + readonly internals: Internals; +>internals : Internals +} +type Out = T["internals"]["out"]; +>Out : Out + +interface ArrayInternals extends Internals[]> {} +interface ArraySchema extends Schema { + readonly internals: ArrayInternals; +>internals : ArrayInternals +} +interface StringInternals extends Internals {} +interface StringSchema extends Schema { + readonly internals: StringInternals; +>internals : StringInternals +} + +type Shape = Readonly<{ [k: string]: Schema }>; +>Shape : Readonly<{ [k: string]: Schema; }> +>k : string + +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +interface ObjectSchema extends Schema { + readonly internals: ObjectInternals; +>internals : ObjectInternals +} + +declare function object(shape: S): ObjectSchema; +>object : (shape: S) => ObjectSchema +>shape : S + +declare function array(element: T): ArraySchema; +>array : (element: T) => ArraySchema +>element : T + +declare function text(): StringSchema; +>text : () => StringSchema + +const node = object({ +>node : ObjectSchema<{ name: StringSchema; readonly children: ArraySchema>; }> +>object({ name: text(), get children() { return array(node); },}) : ObjectSchema<{ name: StringSchema; readonly children: ArraySchema>; }> +>object : (shape: S) => ObjectSchema +>{ name: text(), get children() { return array(node); },} : { name: StringSchema; readonly children: ArraySchema>; }>>; } + + name: text(), +>name : StringSchema +>text() : StringSchema +>text : () => StringSchema + + get children() { +>children : ArraySchema>; }>> + + return array(node); +>array(node) : ArraySchema>; }>> +>array : (element: T) => ArraySchema +>node : ObjectSchema<{ name: StringSchema; readonly children: ArraySchema>; }> + + }, +}); + +type TreeNode = Out; +>TreeNode : { name: string; readonly children: any[]; } +>node : ObjectSchema<{ name: StringSchema; readonly children: ArraySchema>; }> + +declare const sample: TreeNode; +>sample : { name: string; readonly children: any[]; } + +// The recursive member is an array of the same node type, not `any` and not `unknown`. +const leafName: string = sample.name; +>leafName : string +>sample.name : string +>sample : { name: string; readonly children: any[]; } +>name : string + +const nestedName: string = sample.children[0].children[0].name; +>nestedName : string +>sample.children[0].children[0].name : string +>sample.children[0].children[0] : { name: string; readonly children: any[]; } +>sample.children[0].children : { name: string; readonly children: any[]; }[] +>sample.children[0] : { name: string; readonly children: any[]; } +>sample.children : { name: string; readonly children: any[]; }[] +>sample : { name: string; readonly children: any[]; } +>children : { name: string; readonly children: any[]; }[] +>0 : 0 +>children : { name: string; readonly children: any[]; }[] +>0 : 0 +>name : string + +// Those two pass on their own if the whole thing collapsed to `any`, which is exactly the failure +// this test exists to catch, so the recursion point is also probed where `any` cannot hide: an +// unknown key has to be an error, and a known one has to have the type it is declared with. +// @ts-expect-error +sample.children[0].missing; +>sample.children[0].missing : error +>sample.children[0] : { name: string; readonly children: any[]; } +>sample.children : { name: string; readonly children: any[]; }[] +>sample : { name: string; readonly children: any[]; } +>children : { name: string; readonly children: any[]; }[] +>0 : 0 +>missing : any + +// @ts-expect-error +const wrongLeafType: number = sample.children[0].name; +>wrongLeafType : number +>sample.children[0].name : string +>sample.children[0] : { name: string; readonly children: any[]; } +>sample.children : { name: string; readonly children: any[]; }[] +>sample : { name: string; readonly children: any[]; } +>children : { name: string; readonly children: any[]; }[] +>0 : 0 +>name : string + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.errors.txt b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.errors.txt new file mode 100644 index 0000000000000..66f5054ded636 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.errors.txt @@ -0,0 +1,38 @@ +recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts(20,16): error TS2339: Property 'run' does not exist on type 'string'. + + +==== recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts (1 errors) ==== + // The companion to recursiveTypeThroughObjectLiteralGetterAbsentProperty, on the speculative path. + // Inferring `T` from `Wrapper` opens a region, and checking the inferred type against the + // constraint reads `D` while its base is still being instantiated. A conditional asked about `D` in + // that window used to be told every property matched, so `[D] extends [Need]` took the true branch + // and the index signature became `{ run(): void }` -- `d.anything.run()` then went unreported. + // + // The window withholds inherited members and nothing else, so `missing` -- which no base of `D` + // declares -- is genuinely absent even there. The conditional has to take the same branch it would + // take once the table is complete, and the call has to stay an error. + // + // The opposite direction -- a name a base does declare, which has to stay withheld -- is guarded by + // the two hover tests for #62181 and by the mapped-type case, all three of which fail if the miss is + // reported instead. A case written here for it would not reach the skip at all. + + export function run(): void { + force(w); + } + + export function probe(): void { + d.anything.run(); + ~~~ +!!! error TS2339: Property 'run' does not exist on type 'string'. + } + + interface Need { missing: string } + type Guarded = [T] extends [Need] ? { run(): void } : string; + interface Base { [k: string]: Guarded; } + interface D extends Base { own: never; } + interface Wrapper { readonly w: T } + declare function force(x: Wrapper): T; + declare const w: Wrapper; + declare const d: D; + + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.symbols new file mode 100644 index 0000000000000..8f58f07ce533b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.symbols @@ -0,0 +1,83 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts === +// The companion to recursiveTypeThroughObjectLiteralGetterAbsentProperty, on the speculative path. +// Inferring `T` from `Wrapper` opens a region, and checking the inferred type against the +// constraint reads `D` while its base is still being instantiated. A conditional asked about `D` in +// that window used to be told every property matched, so `[D] extends [Need]` took the true branch +// and the index signature became `{ run(): void }` -- `d.anything.run()` then went unreported. +// +// The window withholds inherited members and nothing else, so `missing` -- which no base of `D` +// declares -- is genuinely absent even there. The conditional has to take the same branch it would +// take once the table is complete, and the call has to stay an error. +// +// The opposite direction -- a name a base does declare, which has to stay withheld -- is guarded by +// the two hover tests for #62181 and by the mapped-type case, all three of which fail if the miss is +// reported instead. A case written here for it would not reach the skip at all. + +export function run(): void { +>run : Symbol(run, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 0, 0)) + + force(w); +>force : Symbol(force, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 26, 38)) +>w : Symbol(w, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 28, 13)) +} + +export function probe(): void { +>probe : Symbol(probe, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 16, 1)) + + d.anything.run(); +>d.anything : Symbol(D.__index, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 19)) +>d : Symbol(d, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 29, 13)) +>anything : Symbol(D.__index, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 19)) +} + +interface Need { missing: string } +>Need : Symbol(Need, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 20, 1)) +>missing : Symbol(Need.missing, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 16)) + +type Guarded = [T] extends [Need] ? { run(): void } : string; +>Guarded : Symbol(Guarded, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 34)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 13)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 13)) +>Need : Symbol(Need, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 20, 1)) +>run : Symbol(run, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 40)) + +interface Base { [k: string]: Guarded; } +>Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 64)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 15)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 21)) +>Guarded : Symbol(Guarded, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 34)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 15)) + +interface D extends Base { own: never; } +>D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 46)) +>Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 64)) +>D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 46)) +>own : Symbol(D.own, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 25, 29)) + +interface Wrapper { readonly w: T } +>Wrapper : Symbol(Wrapper, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 25, 43)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 26, 18)) +>w : Symbol(Wrapper.w, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 26, 22)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 26, 18)) + +declare function force(x: Wrapper): T; +>force : Symbol(force, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 26, 38)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 27, 23)) +>own : Symbol(own, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 27, 34)) +>x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 27, 49)) +>Wrapper : Symbol(Wrapper, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 25, 43)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 27, 23)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 27, 23)) + +declare const w: Wrapper; +>w : Symbol(w, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 28, 13)) +>Wrapper : Symbol(Wrapper, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 25, 43)) +>D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 46)) + +declare const d: D; +>d : Symbol(d, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 29, 13)) +>D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 46)) + + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.types new file mode 100644 index 0000000000000..0d5d70d732f9d --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.types @@ -0,0 +1,66 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts === +// The companion to recursiveTypeThroughObjectLiteralGetterAbsentProperty, on the speculative path. +// Inferring `T` from `Wrapper` opens a region, and checking the inferred type against the +// constraint reads `D` while its base is still being instantiated. A conditional asked about `D` in +// that window used to be told every property matched, so `[D] extends [Need]` took the true branch +// and the index signature became `{ run(): void }` -- `d.anything.run()` then went unreported. +// +// The window withholds inherited members and nothing else, so `missing` -- which no base of `D` +// declares -- is genuinely absent even there. The conditional has to take the same branch it would +// take once the table is complete, and the call has to stay an error. +// +// The opposite direction -- a name a base does declare, which has to stay withheld -- is guarded by +// the two hover tests for #62181 and by the mapped-type case, all three of which fail if the miss is +// reported instead. A case written here for it would not reach the skip at all. + +export function run(): void { +>run : () => void + + force(w); +>force(w) : D +>force : (x: Wrapper) => T +>w : Wrapper +} + +export function probe(): void { +>probe : () => void + + d.anything.run(); +>d.anything.run() : any +>d.anything.run : any +>d.anything : string +>d : D +>anything : string +>run : any +} + +interface Need { missing: string } +>missing : string + +type Guarded = [T] extends [Need] ? { run(): void } : string; +>Guarded : Guarded +>run : () => void + +interface Base { [k: string]: Guarded; } +>k : string + +interface D extends Base { own: never; } +>own : never + +interface Wrapper { readonly w: T } +>w : T + +declare function force(x: Wrapper): T; +>force : (x: Wrapper) => T +>own : never +>x : Wrapper + +declare const w: Wrapper; +>w : Wrapper + +declare const d: D; +>d : D + + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.errors.txt b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.errors.txt new file mode 100644 index 0000000000000..e86610d1515ff --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.errors.txt @@ -0,0 +1,27 @@ +recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts(20,12): error TS2339: Property 'run' does not exist on type 'string'. + + +==== recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts (1 errors) ==== + // A property that is genuinely absent has to stay absent. While ObjectFlagsUnresolvedMembers is set + // the member table holds only what the type declares itself, so a miss in that window is treated as + // "not known yet" -- but only inside a speculative region, where the answer is provisional anyway. + // + // Without that restriction this compiles clean: `D` never has `missing`, so the conditional has to + // pick `string` and `.run()` on it is an error. Suppressing the unmatched property during an ordinary + // check makes the conditional take the wrong branch and the call goes unreported. + + interface Need { + missing: string; + } + type Guarded = [T] extends [Need] ? { run(): void } : string; + interface Base { + [k: string]: Guarded; + } + interface D extends Base { + own: never; + } + declare const d: D; + d.anything.run(); + ~~~ +!!! error TS2339: Property 'run' does not exist on type 'string'. + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.symbols new file mode 100644 index 0000000000000..839e83976009b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.symbols @@ -0,0 +1,50 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts === +// A property that is genuinely absent has to stay absent. While ObjectFlagsUnresolvedMembers is set +// the member table holds only what the type declares itself, so a miss in that window is treated as +// "not known yet" -- but only inside a speculative region, where the answer is provisional anyway. +// +// Without that restriction this compiles clean: `D` never has `missing`, so the conditional has to +// pick `string` and `.run()` on it is an error. Suppressing the unmatched property during an ordinary +// check makes the conditional take the wrong branch and the call goes unreported. + +interface Need { +>Need : Symbol(Need, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 0, 0)) + + missing: string; +>missing : Symbol(Need.missing, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 8, 16)) +} +type Guarded = [T] extends [Need] ? { run(): void } : string; +>Guarded : Symbol(Guarded, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 10, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 11, 13)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 11, 13)) +>Need : Symbol(Need, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 0, 0)) +>run : Symbol(run, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 11, 40)) + +interface Base { +>Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 11, 64)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 12, 15)) + + [k: string]: Guarded; +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 13, 5)) +>Guarded : Symbol(Guarded, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 10, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 12, 15)) +} +interface D extends Base { +>D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 14, 1)) +>Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 11, 64)) +>D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 14, 1)) + + own: never; +>own : Symbol(D.own, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 15, 29)) +} +declare const d: D; +>d : Symbol(d, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 18, 13)) +>D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 14, 1)) + +d.anything.run(); +>d.anything : Symbol(D.__index, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 12, 19)) +>d : Symbol(d, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 18, 13)) +>anything : Symbol(D.__index, Decl(recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts, 12, 19)) + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.types new file mode 100644 index 0000000000000..badca38e42f47 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.types @@ -0,0 +1,38 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts === +// A property that is genuinely absent has to stay absent. While ObjectFlagsUnresolvedMembers is set +// the member table holds only what the type declares itself, so a miss in that window is treated as +// "not known yet" -- but only inside a speculative region, where the answer is provisional anyway. +// +// Without that restriction this compiles clean: `D` never has `missing`, so the conditional has to +// pick `string` and `.run()` on it is an error. Suppressing the unmatched property during an ordinary +// check makes the conditional take the wrong branch and the call goes unreported. + +interface Need { + missing: string; +>missing : string +} +type Guarded = [T] extends [Need] ? { run(): void } : string; +>Guarded : Guarded +>run : () => void + +interface Base { + [k: string]: Guarded; +>k : string +} +interface D extends Base { + own: never; +>own : never +} +declare const d: D; +>d : D + +d.anything.run(); +>d.anything.run() : any +>d.anything.run : any +>d.anything : string +>d : D +>anything : string +>run : any + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.errors.txt b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.errors.txt new file mode 100644 index 0000000000000..3c3405cb2fa92 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.errors.txt @@ -0,0 +1,27 @@ +recursiveTypeThroughObjectLiteralGetterConstraint.ts(15,9): error TS2741: Property 'out' is missing in type 'Schema[]' but required in type 'Schema'. + + +==== recursiveTypeThroughObjectLiteralGetterConstraint.ts (1 errors) ==== + // Skipping an accessor during a check that reports nothing is a deferral, not a waiver. An array of + // schemas is not a Schema, so this reports. The baseline is byte-identical to what an unpatched + // compiler produces, and that is the whole point of the case: it pins that the constraint is still + // enforced, not which pass enforced it. The recursive shape where the deferred pass is the only thing + // that can report it is recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts. + + interface Schema { + readonly out: O; + } + type Shape = Record>; + declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; + declare const leaf: Schema; + + const flat = object({ + get bad() { + ~~~ +!!! error TS2741: Property 'out' is missing in type 'Schema[]' but required in type 'Schema'. +!!! related TS2728 recursiveTypeThroughObjectLiteralGetterConstraint.ts:8:14: 'out' is declared here. + return [leaf]; + }, + }); + export const out = flat.out; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.symbols new file mode 100644 index 0000000000000..9de6d7b8a1713 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.symbols @@ -0,0 +1,58 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterConstraint.ts === +// Skipping an accessor during a check that reports nothing is a deferral, not a waiver. An array of +// schemas is not a Schema, so this reports. The baseline is byte-identical to what an unpatched +// compiler produces, and that is the whole point of the case: it pins that the constraint is still +// enforced, not which pass enforced it. The recursive shape where the deferred pass is the only thing +// that can report it is recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts. + +interface Schema { +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 0, 0)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 6, 17)) + + readonly out: O; +>out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 6, 21)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 6, 17)) +} +type Shape = Record>; +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 8, 1)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 0, 0)) + +declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 9, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 10, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 8, 1)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 10, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 10, 24)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 0, 0)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 10, 62)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 10, 24)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 10, 24)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 10, 62)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 10, 95)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 10, 24)) + +declare const leaf: Schema; +>leaf : Symbol(leaf, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 11, 13)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 0, 0)) + +const flat = object({ +>flat : Symbol(flat, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 13, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 9, 41)) + + get bad() { +>bad : Symbol(bad, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 13, 21)) + + return [leaf]; +>leaf : Symbol(leaf, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 11, 13)) + + }, +}); +export const out = flat.out; +>out : Symbol(out, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 18, 12)) +>flat.out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 6, 21)) +>flat : Symbol(flat, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 13, 5)) +>out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterConstraint.ts, 6, 21)) + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.types new file mode 100644 index 0000000000000..e3e78e9315ac5 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.types @@ -0,0 +1,45 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterConstraint.ts === +// Skipping an accessor during a check that reports nothing is a deferral, not a waiver. An array of +// schemas is not a Schema, so this reports. The baseline is byte-identical to what an unpatched +// compiler produces, and that is the whole point of the case: it pins that the constraint is still +// enforced, not which pass enforced it. The recursive shape where the deferred pass is the only thing +// that can report it is recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts. + +interface Schema { + readonly out: O; +>out : O +} +type Shape = Record>; +>Shape : Shape + +declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>shape : S +>shape : S + +declare const leaf: Schema; +>leaf : Schema + +const flat = object({ +>flat : Schema<{ [x: string]: any; }> & { shape: Shape; } +>object({ get bad() { return [leaf]; },}) : Schema<{ [x: string]: any; }> & { shape: Shape; } +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>{ get bad() { return [leaf]; },} : { readonly bad: Schema[]; } + + get bad() { +>bad : Schema[] + + return [leaf]; +>[leaf] : Schema[] +>leaf : Schema + + }, +}); +export const out = flat.out; +>out : { [x: string]: any; } +>flat.out : { [x: string]: any; } +>flat : Schema<{ [x: string]: any; }> & { shape: Shape; } +>out : { [x: string]: any; } + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.errors.txt b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.errors.txt new file mode 100644 index 0000000000000..d68c2c24fda26 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.errors.txt @@ -0,0 +1,24 @@ +schema.ts(8,9): error TS2741: Property 'out' is missing in type '(Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; })[]' but required in type 'Schema'. + + +==== schema.ts (1 errors) ==== + export interface Schema { + readonly out: O; + } + export type Shape = Record>; + export declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; + + export const tree = object({ + get bad() { + ~~~ +!!! error TS2741: Property 'out' is missing in type '(Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; })[]' but required in type 'Schema'. +!!! related TS2728 schema.ts:2:14: 'out' is declared here. + return [tree]; + }, + }); + +==== consumer.ts (0 errors) ==== + import { tree } from "./schema.js"; + + export const out = tree.out; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.symbols new file mode 100644 index 0000000000000..92132cbf532bc --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.symbols @@ -0,0 +1,53 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.ts] //// + +=== schema.ts === +export interface Schema { +>Schema : Symbol(Schema, Decl(schema.ts, 0, 0)) +>O : Symbol(O, Decl(schema.ts, 0, 24)) + + readonly out: O; +>out : Symbol(Schema.out, Decl(schema.ts, 0, 28)) +>O : Symbol(O, Decl(schema.ts, 0, 24)) +} +export type Shape = Record>; +>Shape : Symbol(Shape, Decl(schema.ts, 2, 1)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>Schema : Symbol(Schema, Decl(schema.ts, 0, 0)) + +export declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; +>object : Symbol(object, Decl(schema.ts, 3, 48)) +>S : Symbol(S, Decl(schema.ts, 4, 31)) +>Shape : Symbol(Shape, Decl(schema.ts, 2, 1)) +>shape : Symbol(shape, Decl(schema.ts, 4, 48)) +>S : Symbol(S, Decl(schema.ts, 4, 31)) +>Schema : Symbol(Schema, Decl(schema.ts, 0, 0)) +>K : Symbol(K, Decl(schema.ts, 4, 69)) +>S : Symbol(S, Decl(schema.ts, 4, 31)) +>S : Symbol(S, Decl(schema.ts, 4, 31)) +>K : Symbol(K, Decl(schema.ts, 4, 69)) +>shape : Symbol(shape, Decl(schema.ts, 4, 102)) +>S : Symbol(S, Decl(schema.ts, 4, 31)) + +export const tree = object({ +>tree : Symbol(tree, Decl(schema.ts, 6, 12)) +>object : Symbol(object, Decl(schema.ts, 3, 48)) + + get bad() { +>bad : Symbol(bad, Decl(schema.ts, 6, 28)) + + return [tree]; +>tree : Symbol(tree, Decl(schema.ts, 6, 12)) + + }, +}); + +=== consumer.ts === +import { tree } from "./schema.js"; +>tree : Symbol(tree, Decl(consumer.ts, 0, 8)) + +export const out = tree.out; +>out : Symbol(out, Decl(consumer.ts, 2, 12)) +>tree.out : Symbol(Schema.out, Decl(schema.ts, 0, 28)) +>tree : Symbol(tree, Decl(consumer.ts, 0, 8)) +>out : Symbol(Schema.out, Decl(schema.ts, 0, 28)) + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.types new file mode 100644 index 0000000000000..05d14bd5ee5ed --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.types @@ -0,0 +1,41 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.ts] //// + +=== schema.ts === +export interface Schema { + readonly out: O; +>out : O +} +export type Shape = Record>; +>Shape : Shape + +export declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>shape : S +>shape : S + +export const tree = object({ +>tree : Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; } +>object({ get bad() { return [tree]; },}) : Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; } +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>{ get bad() { return [tree]; },} : { readonly bad: (Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; })[]; } + + get bad() { +>bad : (Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; })[] + + return [tree]; +>[tree] : (Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; })[] +>tree : Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; } + + }, +}); + +=== consumer.ts === +import { tree } from "./schema.js"; +>tree : import("./schema.js").Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (import("./schema.js").Schema<{ readonly bad: unknown; }> & any)[]; }; } + +export const out = tree.out; +>out : { readonly bad: unknown; } +>tree.out : { readonly bad: unknown; } +>tree : import("./schema.js").Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (import("./schema.js").Schema<{ readonly bad: unknown; }> & any)[]; }; } +>out : { readonly bad: unknown; } + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.js b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.js new file mode 100644 index 0000000000000..f04283c6f0851 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.js @@ -0,0 +1,79 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts] //// + +//// [recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts] +// What the recursive schema looks like coming out as a declaration. On main this is +// `export declare const node: any` plus two implicit-any errors, because the type never resolved. +// The recursion point itself still comes out as `/*elided*/ any` -- the node builder has no way to +// write a reference back into the type it is in the middle of writing, and main elides the same way +// on a type it can resolve. Everything around it is now the real shape. + +interface Internals { + readonly out: O; +} +interface Schema { + readonly internals: Internals; +} +type Out = T["internals"]["out"]; + +interface ArrayInternals extends Internals[]> {} +interface ArraySchema extends Schema { + readonly internals: ArrayInternals; +} +interface StringInternals extends Internals {} +interface StringSchema extends Schema { + readonly internals: StringInternals; +} + +type Shape = Readonly<{ [k: string]: Schema }>; +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +interface ObjectSchema extends Schema { + readonly internals: ObjectInternals; +} + +declare function object(shape: S): ObjectSchema; +declare function array(element: T): ArraySchema; +declare function text(): StringSchema; + +export const node = object({ + name: text(), + get children() { + return array(node); + }, +}); + + + + +//// [recursiveTypeThroughObjectLiteralGetterDeclarationEmit.d.ts] +interface Internals { + readonly out: O; +} +interface Schema { + readonly internals: Internals; +} +type Out = T["internals"]["out"]; +interface ArrayInternals extends Internals[]> { +} +interface ArraySchema extends Schema { + readonly internals: ArrayInternals; +} +interface StringInternals extends Internals { +} +interface StringSchema extends Schema { + readonly internals: StringInternals; +} +type Shape = Readonly<{ + [k: string]: Schema; +}>; +interface ObjectInternals extends Internals<{ + [K in keyof S]: Out; +}> { +} +interface ObjectSchema extends Schema { + readonly internals: ObjectInternals; +} +export declare const node: ObjectSchema<{ + name: StringSchema; + readonly children: ArraySchema>; +}>; +export {}; diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.symbols new file mode 100644 index 0000000000000..8b81e0d2dd8af --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.symbols @@ -0,0 +1,133 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts === +// What the recursive schema looks like coming out as a declaration. On main this is +// `export declare const node: any` plus two implicit-any errors, because the type never resolved. +// The recursion point itself still comes out as `/*elided*/ any` -- the node builder has no way to +// write a reference back into the type it is in the middle of writing, and main elides the same way +// on a type it can resolve. Everything around it is now the real shape. + +interface Internals { +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 0, 0)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 6, 20)) + + readonly out: O; +>out : Symbol(Internals.out, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 6, 38)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 6, 20)) +} +interface Schema { +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 8, 1)) + + readonly internals: Internals; +>internals : Symbol(Schema.internals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 9, 18)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 0, 0)) +} +type Out = T["internals"]["out"]; +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 11, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 12, 9)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 8, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 12, 9)) + +interface ArrayInternals extends Internals[]> {} +>ArrayInternals : Symbol(ArrayInternals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 12, 51)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 14, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 8, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 0, 0)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 11, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 14, 25)) + +interface ArraySchema extends Schema { +>ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 14, 73)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 15, 22)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 8, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 8, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 8, 1)) + + readonly internals: ArrayInternals; +>internals : Symbol(ArraySchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 15, 65)) +>ArrayInternals : Symbol(ArrayInternals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 12, 51)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 15, 22)) +} +interface StringInternals extends Internals {} +>StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 17, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 0, 0)) + +interface StringSchema extends Schema { +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 18, 54)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 8, 1)) + + readonly internals: StringInternals; +>internals : Symbol(StringSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 19, 39)) +>StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 17, 1)) +} + +type Shape = Readonly<{ [k: string]: Schema }>; +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 21, 1)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 23, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 8, 1)) + +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +>ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 23, 47)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 24, 26)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 21, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 0, 0)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 24, 64)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 24, 26)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 11, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 24, 26)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 24, 64)) + +interface ObjectSchema extends Schema { +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 24, 94)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 25, 23)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 21, 1)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 21, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 8, 1)) + + readonly internals: ObjectInternals; +>internals : Symbol(ObjectSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 25, 64)) +>ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 23, 47)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 25, 23)) +} + +declare function object(shape: S): ObjectSchema; +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 27, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 29, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 21, 1)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 29, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 29, 24)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 24, 94)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 29, 24)) + +declare function array(element: T): ArraySchema; +>array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 29, 68)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 30, 23)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 8, 1)) +>element : Symbol(element, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 30, 41)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 30, 23)) +>ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 14, 73)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 30, 23)) + +declare function text(): StringSchema; +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 30, 69)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 18, 54)) + +export const node = object({ +>node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 33, 12)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 27, 1)) + + name: text(), +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 33, 28)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 30, 69)) + + get children() { +>children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 34, 17)) + + return array(node); +>array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 29, 68)) +>node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts, 33, 12)) + + }, +}); + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.types new file mode 100644 index 0000000000000..217a501cb5ae1 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.types @@ -0,0 +1,74 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts === +// What the recursive schema looks like coming out as a declaration. On main this is +// `export declare const node: any` plus two implicit-any errors, because the type never resolved. +// The recursion point itself still comes out as `/*elided*/ any` -- the node builder has no way to +// write a reference back into the type it is in the middle of writing, and main elides the same way +// on a type it can resolve. Everything around it is now the real shape. + +interface Internals { + readonly out: O; +>out : O +} +interface Schema { + readonly internals: Internals; +>internals : Internals +} +type Out = T["internals"]["out"]; +>Out : Out + +interface ArrayInternals extends Internals[]> {} +interface ArraySchema extends Schema { + readonly internals: ArrayInternals; +>internals : ArrayInternals +} +interface StringInternals extends Internals {} +interface StringSchema extends Schema { + readonly internals: StringInternals; +>internals : StringInternals +} + +type Shape = Readonly<{ [k: string]: Schema }>; +>Shape : Readonly<{ [k: string]: Schema; }> +>k : string + +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +interface ObjectSchema extends Schema { + readonly internals: ObjectInternals; +>internals : ObjectInternals +} + +declare function object(shape: S): ObjectSchema; +>object : (shape: S) => ObjectSchema +>shape : S + +declare function array(element: T): ArraySchema; +>array : (element: T) => ArraySchema +>element : T + +declare function text(): StringSchema; +>text : () => StringSchema + +export const node = object({ +>node : ObjectSchema<{ name: StringSchema; readonly children: ArraySchema>; }> +>object({ name: text(), get children() { return array(node); },}) : ObjectSchema<{ name: StringSchema; readonly children: ArraySchema>; }> +>object : (shape: S) => ObjectSchema +>{ name: text(), get children() { return array(node); },} : { name: StringSchema; readonly children: ArraySchema>; }>>; } + + name: text(), +>name : StringSchema +>text() : StringSchema +>text : () => StringSchema + + get children() { +>children : ArraySchema>; }>> + + return array(node); +>array(node) : ArraySchema>; }>> +>array : (element: T) => ArraySchema +>node : ObjectSchema<{ name: StringSchema; readonly children: ArraySchema>; }> + + }, +}); + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.errors.txt b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.errors.txt new file mode 100644 index 0000000000000..c466d15278fa3 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.errors.txt @@ -0,0 +1,77 @@ +recursiveTypeThroughObjectLiteralGetterInheritedMember.ts(61,43): error TS2339: Property 'name' does not exist on type '{}'. + + +==== recursiveTypeThroughObjectLiteralGetterInheritedMember.ts (1 errors) ==== + // The shape from #62180, where the recursive member is reached through a mapped type that remaps its + // keys by reading each member's own discriminant. Resolving the object schema's members instantiates + // its base with a type that names the schema back, so the schema's table is read while it is still + // being assembled and its inherited `out` is not in it yet. + // + // This case is NOT fixed, and the baseline records how far it gets. An earlier draft answered every + // miss in that window with `any`, which cleared the error here -- but that answer is not confined to + // this shape. A self-referential interface reaches the same window with no getter and no inference + // anywhere in the program, and an unpatched compiler answers it from the index signature; making it + // `any` silently accepted an indexed access that every other compiler rejects. Nothing available at + // the miss distinguishes the two, so the answer was withdrawn rather than guessed. + // + // What is left is still an improvement on main, which cannot type the declaration at all: the + // getter resolves, the non-recursive members are real types, and only the remapped member is + // unresolved. + + interface Internals { + optional: "true" | "false"; + out: O; + } + + interface StringSchema extends Internals { + optional: "false"; + } + + type Shape = Record; + type Prettify = { [K in keyof T]: T[K] } & {}; + type ObjectOut = Prettify< + { + [K in keyof S as S[K] extends { optional: "true" } ? K : never]?: S[K]["out"]; + } & { + [K in keyof S as S[K] extends { optional: "true" } ? never : K]: S[K]["out"]; + } + >; + + interface ObjectSchema extends Internals> { + optional: "false"; + } + + interface OptionalSchema> extends Internals { + optional: "true"; + } + + declare function object(shape: S): ObjectSchema; + declare function text(): StringSchema; + declare function optional>(schema: T): OptionalSchema; + + const category = object({ + name: text(), + get parent() { + return optional(category); + }, + }); + + declare const sample: (typeof category)["out"]; + // The key that does not recurse resolves fully, and the remapping put the recursive one in the + // optional bucket, so it is `parent?:` rather than `parent:`. Both of those are new: main gives the + // whole declaration an implicit `any` and reports it. + const topName: string = sample.name; + // The recursive one does not resolve. Pinned so that closing this shows up here. + const parentName: string = sample.parent!.name; + ~~~~ +!!! error TS2339: Property 'name' does not exist on type '{}'. + + // A property that is genuinely absent must still be reported, whoever is asking. + interface Base { readonly own: number; } + interface Derived extends Base { readonly extra: string; } + declare const derived: Derived; + // @ts-expect-error + derived.missing; + const ownValue: number = derived.own; + const extraValue: string = derived.extra; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols new file mode 100644 index 0000000000000..7fee19cde7e3c --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols @@ -0,0 +1,193 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterInheritedMember.ts === +// The shape from #62180, where the recursive member is reached through a mapped type that remaps its +// keys by reading each member's own discriminant. Resolving the object schema's members instantiates +// its base with a type that names the schema back, so the schema's table is read while it is still +// being assembled and its inherited `out` is not in it yet. +// +// This case is NOT fixed, and the baseline records how far it gets. An earlier draft answered every +// miss in that window with `any`, which cleared the error here -- but that answer is not confined to +// this shape. A self-referential interface reaches the same window with no getter and no inference +// anywhere in the program, and an unpatched compiler answers it from the index signature; making it +// `any` silently accepted an indexed access that every other compiler rejects. Nothing available at +// the miss distinguishes the two, so the answer was withdrawn rather than guessed. +// +// What is left is still an improvement on main, which cannot type the declaration at all: the +// getter resolves, the non-recursive members are real types, and only the remapped member is +// unresolved. + +interface Internals { +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 0, 0)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 16, 20)) + + optional: "true" | "false"; +>optional : Symbol(Internals.optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 16, 24)) + + out: O; +>out : Symbol(Internals.out, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 17, 31)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 16, 20)) +} + +interface StringSchema extends Internals { +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 19, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 0, 0)) + + optional: "false"; +>optional : Symbol(StringSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 21, 50)) +} + +type Shape = Record; +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 23, 1)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +type Prettify = { [K in keyof T]: T[K] } & {}; +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 25, 33)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 14)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 22)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 14)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 14)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 22)) + +type ObjectOut = Prettify< +>ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 49)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 23, 1)) +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 25, 33)) + { + [K in keyof S as S[K] extends { optional: "true" } ? K : never]?: S[K]["out"]; +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 29, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 29, 9)) +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 29, 39)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 29, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 29, 9)) + + } & { + [K in keyof S as S[K] extends { optional: "true" } ? never : K]: S[K]["out"]; +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 31, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 31, 9)) +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 31, 39)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 31, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 31, 9)) + } +>; + +interface ObjectSchema extends Internals> { +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 33, 2)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 35, 23)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 23, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 0, 0)) +>ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 49)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 35, 23)) + + optional: "false"; +>optional : Symbol(ObjectSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 35, 73)) +} + +interface OptionalSchema> extends Internals { +>OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 37, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 39, 25)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 0, 0)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 39, 25)) + + optional: "true"; +>optional : Symbol(OptionalSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 39, 92)) +} + +declare function object(shape: S): ObjectSchema; +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 41, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 23, 1)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 24)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 33, 2)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 24)) + +declare function text(): StringSchema; +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 68)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 19, 1)) + +declare function optional>(schema: T): OptionalSchema; +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 44, 38)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 45, 26)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 0, 0)) +>schema : Symbol(schema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 45, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 45, 26)) +>OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 37, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 45, 26)) + +const category = object({ +>category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 41, 1)) + + name: text(), +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 68)) + + get parent() { +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) + + return optional(category); +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 44, 38)) +>category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 5)) + + }, +}); + +declare const sample: (typeof category)["out"]; +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) +>category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 5)) + +// The key that does not recurse resolves fully, and the remapping put the recursive one in the +// optional bucket, so it is `parent?:` rather than `parent:`. Both of those are new: main gives the +// whole declaration an implicit `any` and reports it. +const topName: string = sample.name; +>topName : Symbol(topName, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 58, 5)) +>sample.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) + +// The recursive one does not resolve. Pinned so that closing this shows up here. +const parentName: string = sample.parent!.name; +>parentName : Symbol(parentName, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 60, 5)) +>sample.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) + +// A property that is genuinely absent must still be reported, whoever is asking. +interface Base { readonly own: number; } +>Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 60, 47)) +>own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 16)) + +interface Derived extends Base { readonly extra: string; } +>Derived : Symbol(Derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 40)) +>Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 60, 47)) +>extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 64, 32)) + +declare const derived: Derived; +>derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 13)) +>Derived : Symbol(Derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 40)) + +// @ts-expect-error +derived.missing; +>derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 13)) + +const ownValue: number = derived.own; +>ownValue : Symbol(ownValue, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 68, 5)) +>derived.own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 16)) +>derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 13)) +>own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 16)) + +const extraValue: string = derived.extra; +>extraValue : Symbol(extraValue, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 69, 5)) +>derived.extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 64, 32)) +>derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 13)) +>extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 64, 32)) + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types new file mode 100644 index 0000000000000..57635cce272dc --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types @@ -0,0 +1,144 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterInheritedMember.ts === +// The shape from #62180, where the recursive member is reached through a mapped type that remaps its +// keys by reading each member's own discriminant. Resolving the object schema's members instantiates +// its base with a type that names the schema back, so the schema's table is read while it is still +// being assembled and its inherited `out` is not in it yet. +// +// This case is NOT fixed, and the baseline records how far it gets. An earlier draft answered every +// miss in that window with `any`, which cleared the error here -- but that answer is not confined to +// this shape. A self-referential interface reaches the same window with no getter and no inference +// anywhere in the program, and an unpatched compiler answers it from the index signature; making it +// `any` silently accepted an indexed access that every other compiler rejects. Nothing available at +// the miss distinguishes the two, so the answer was withdrawn rather than guessed. +// +// What is left is still an improvement on main, which cannot type the declaration at all: the +// getter resolves, the non-recursive members are real types, and only the remapped member is +// unresolved. + +interface Internals { + optional: "true" | "false"; +>optional : "false" | "true" + + out: O; +>out : O +} + +interface StringSchema extends Internals { + optional: "false"; +>optional : "false" +} + +type Shape = Record; +>Shape : Shape + +type Prettify = { [K in keyof T]: T[K] } & {}; +>Prettify : { [K in keyof T]: T[K]; } + +type ObjectOut = Prettify< +>ObjectOut : { [K in keyof S as S[K] extends { optional: "true"; } ? K : never]?: S[K]["out"] | undefined; } & { [K in keyof S as S[K] extends { optional: "true"; } ? never : K]: S[K]["out"]; } extends infer T ? { [K in keyof T]: T[K]; } : never + { + [K in keyof S as S[K] extends { optional: "true" } ? K : never]?: S[K]["out"]; +>optional : "true" + + } & { + [K in keyof S as S[K] extends { optional: "true" } ? never : K]: S[K]["out"]; +>optional : "true" + } +>; + +interface ObjectSchema extends Internals> { + optional: "false"; +>optional : "false" +} + +interface OptionalSchema> extends Internals { + optional: "true"; +>optional : "true" +} + +declare function object(shape: S): ObjectSchema; +>object : (shape: S) => ObjectSchema +>shape : S + +declare function text(): StringSchema; +>text : () => StringSchema + +declare function optional>(schema: T): OptionalSchema; +>optional : >(schema: T) => OptionalSchema +>schema : T + +const category = object({ +>category : ObjectSchema<{ name: StringSchema; readonly parent: OptionalSchema>; }> +>object({ name: text(), get parent() { return optional(category); },}) : ObjectSchema<{ name: StringSchema; readonly parent: OptionalSchema>; }> +>object : (shape: S) => ObjectSchema +>{ name: text(), get parent() { return optional(category); },} : { name: StringSchema; readonly parent: OptionalSchema>; }>>; } + + name: text(), +>name : StringSchema +>text() : StringSchema +>text : () => StringSchema + + get parent() { +>parent : OptionalSchema>; }>> + + return optional(category); +>optional(category) : OptionalSchema>; }>> +>optional : >(schema: T) => OptionalSchema +>category : ObjectSchema<{ name: StringSchema; readonly parent: OptionalSchema>; }> + + }, +}); + +declare const sample: (typeof category)["out"]; +>sample : { name: string; readonly parent?: unknown; } +>category : ObjectSchema<{ name: StringSchema; readonly parent: OptionalSchema>; }> + +// The key that does not recurse resolves fully, and the remapping put the recursive one in the +// optional bucket, so it is `parent?:` rather than `parent:`. Both of those are new: main gives the +// whole declaration an implicit `any` and reports it. +const topName: string = sample.name; +>topName : string +>sample.name : string +>sample : { name: string; readonly parent?: unknown; } +>name : string + +// The recursive one does not resolve. Pinned so that closing this shows up here. +const parentName: string = sample.parent!.name; +>parentName : string +>sample.parent!.name : any +>sample.parent! : {} +>sample.parent : unknown +>sample : { name: string; readonly parent?: unknown; } +>parent : unknown +>name : any + +// A property that is genuinely absent must still be reported, whoever is asking. +interface Base { readonly own: number; } +>own : number + +interface Derived extends Base { readonly extra: string; } +>extra : string + +declare const derived: Derived; +>derived : Derived + +// @ts-expect-error +derived.missing; +>derived.missing : any +>derived : Derived +>missing : any + +const ownValue: number = derived.own; +>ownValue : number +>derived.own : number +>derived : Derived +>own : number + +const extraValue: string = derived.extra; +>extraValue : string +>derived.extra : string +>derived : Derived +>extra : string + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.symbols new file mode 100644 index 0000000000000..f02e1edc5463b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.symbols @@ -0,0 +1,104 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterOverloads.ts === +// Whether a get accessor has been resolved records what has been asked for so far, not anything +// about the program, so skipping one on that alone makes an ordinary comparison depend on +// resolution order. This picked the numeric overload for an object whose only member is a string. +// +// The generic pair at the bottom matters more than the plain one: only a generic signature opens the +// speculative region where the skip is allowed at all, so the non-generic overloads never reach the +// code under test. Skipping there made the object look like it had no string member and picked the +// numeric candidate again, this time with the region wide open. + +declare function pick(x: { [k: string]: number }): "num"; +>pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 57)) +>x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 22)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 28)) + +declare function pick(x: object): "obj"; +>pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 57)) +>x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 10, 22)) + +const inferred = pick({ +>inferred : Symbol(inferred, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 12, 5)) +>pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 57)) + + get s() { +>s : Symbol(s, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 12, 23)) + + return "hello"; + }, +}); +const mustBeObj: "obj" = inferred; +>mustBeObj : Symbol(mustBeObj, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 17, 5)) +>inferred : Symbol(inferred, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 12, 5)) + +const annotated = pick({ +>annotated : Symbol(annotated, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 19, 5)) +>pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 57)) + + get s(): string { +>s : Symbol(s, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 19, 24)) + + return "hello"; + }, +}); +const alsoObj: "obj" = annotated; +>alsoObj : Symbol(alsoObj, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 24, 5)) +>annotated : Symbol(annotated, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 19, 5)) + +const numeric = pick({ +>numeric : Symbol(numeric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 26, 5)) +>pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 57)) + + get n() { +>n : Symbol(n, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 26, 22)) + + return 1; + }, +}); +const mustBeNum: "num" = numeric; +>mustBeNum : Symbol(mustBeNum, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 31, 5)) +>numeric : Symbol(numeric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 26, 5)) + +declare function generic(x: T): "num"; +>generic : Symbol(generic, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 31, 33), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 73)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 25)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 38)) +>x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 60)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 25)) + +declare function generic(x: T): "obj"; +>generic : Symbol(generic, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 31, 33), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 73)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 34, 25)) +>x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 34, 43)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 34, 25)) + +const inferredGeneric = generic({ +>inferredGeneric : Symbol(inferredGeneric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 36, 5)) +>generic : Symbol(generic, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 31, 33), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 73)) + + get s() { +>s : Symbol(s, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 36, 33)) + + return "hello"; + }, +}); +const genericMustBeObj: "obj" = inferredGeneric; +>genericMustBeObj : Symbol(genericMustBeObj, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 41, 5)) +>inferredGeneric : Symbol(inferredGeneric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 36, 5)) + +const numericGeneric = generic({ +>numericGeneric : Symbol(numericGeneric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 43, 5)) +>generic : Symbol(generic, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 31, 33), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 73)) + + get n() { +>n : Symbol(n, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 43, 32)) + + return 1; + }, +}); +const genericMustBeNum: "num" = numericGeneric; +>genericMustBeNum : Symbol(genericMustBeNum, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 48, 5)) +>numericGeneric : Symbol(numericGeneric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 43, 5)) + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.types new file mode 100644 index 0000000000000..1725e348cbcef --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.types @@ -0,0 +1,120 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterOverloads.ts === +// Whether a get accessor has been resolved records what has been asked for so far, not anything +// about the program, so skipping one on that alone makes an ordinary comparison depend on +// resolution order. This picked the numeric overload for an object whose only member is a string. +// +// The generic pair at the bottom matters more than the plain one: only a generic signature opens the +// speculative region where the skip is allowed at all, so the non-generic overloads never reach the +// code under test. Skipping there made the object look like it had no string member and picked the +// numeric candidate again, this time with the region wide open. + +declare function pick(x: { [k: string]: number }): "num"; +>pick : { (x: { [k: string]: number; }): "num"; (x: object): "obj"; } +>x : { [k: string]: number; } +>k : string + +declare function pick(x: object): "obj"; +>pick : { (x: { [k: string]: number; }): "num"; (x: object): "obj"; } +>x : object + +const inferred = pick({ +>inferred : "obj" +>pick({ get s() { return "hello"; },}) : "obj" +>pick : { (x: { [k: string]: number; }): "num"; (x: object): "obj"; } +>{ get s() { return "hello"; },} : { readonly s: string; } + + get s() { +>s : string + + return "hello"; +>"hello" : "hello" + + }, +}); +const mustBeObj: "obj" = inferred; +>mustBeObj : "obj" +>inferred : "obj" + +const annotated = pick({ +>annotated : "obj" +>pick({ get s(): string { return "hello"; },}) : "obj" +>pick : { (x: { [k: string]: number; }): "num"; (x: object): "obj"; } +>{ get s(): string { return "hello"; },} : { readonly s: string; } + + get s(): string { +>s : string + + return "hello"; +>"hello" : "hello" + + }, +}); +const alsoObj: "obj" = annotated; +>alsoObj : "obj" +>annotated : "obj" + +const numeric = pick({ +>numeric : "num" +>pick({ get n() { return 1; },}) : "num" +>pick : { (x: { [k: string]: number; }): "num"; (x: object): "obj"; } +>{ get n() { return 1; },} : { readonly n: number; } + + get n() { +>n : number + + return 1; +>1 : 1 + + }, +}); +const mustBeNum: "num" = numeric; +>mustBeNum : "num" +>numeric : "num" + +declare function generic(x: T): "num"; +>generic : { (x: T): "num"; (x: T_1): "obj"; } +>k : string +>x : T + +declare function generic(x: T): "obj"; +>generic : { (x: T_1): "num"; (x: T): "obj"; } +>x : T + +const inferredGeneric = generic({ +>inferredGeneric : "obj" +>generic({ get s() { return "hello"; },}) : "obj" +>generic : { (x: T): "num"; (x: T): "obj"; } +>{ get s() { return "hello"; },} : { readonly s: string; } + + get s() { +>s : string + + return "hello"; +>"hello" : "hello" + + }, +}); +const genericMustBeObj: "obj" = inferredGeneric; +>genericMustBeObj : "obj" +>inferredGeneric : "obj" + +const numericGeneric = generic({ +>numericGeneric : "num" +>generic({ get n() { return 1; },}) : "num" +>generic : { (x: T): "num"; (x: T): "obj"; } +>{ get n() { return 1; },} : { readonly n: number; } + + get n() { +>n : number + + return 1; +>1 : 1 + + }, +}); +const genericMustBeNum: "num" = numericGeneric; +>genericMustBeNum : "num" +>numericGeneric : "num" + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.errors.txt b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.errors.txt new file mode 100644 index 0000000000000..2371c79e83360 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.errors.txt @@ -0,0 +1,24 @@ +recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts(12,9): error TS2741: Property 'out' is missing in type '(Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; })[]' but required in type 'Schema'. + + +==== recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts (1 errors) ==== + // Skipping a member that genuinely cannot be worked out yet postpones the constraint check; it does + // not waive it. The getter names the declaration being resolved, so the skip is right, but what it + // returns still violates the constraint and that has to surface once the declaration has a type. + + interface Schema { + readonly out: O; + } + type Shape = Record>; + declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; + + const tree = object({ + get bad() { + ~~~ +!!! error TS2741: Property 'out' is missing in type '(Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; })[]' but required in type 'Schema'. +!!! related TS2728 recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts:6:14: 'out' is declared here. + return [tree]; + }, + }); + export const out = tree.out; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.symbols new file mode 100644 index 0000000000000..727f75aec6943 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.symbols @@ -0,0 +1,52 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts === +// Skipping a member that genuinely cannot be worked out yet postpones the constraint check; it does +// not waive it. The getter names the declaration being resolved, so the skip is right, but what it +// returns still violates the constraint and that has to surface once the declaration has a type. + +interface Schema { +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 0, 0)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 4, 17)) + + readonly out: O; +>out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 4, 21)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 4, 17)) +} +type Shape = Record>; +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 6, 1)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 0, 0)) + +declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 7, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 8, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 6, 1)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 8, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 8, 24)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 0, 0)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 8, 62)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 8, 24)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 8, 24)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 8, 62)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 8, 95)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 8, 24)) + +const tree = object({ +>tree : Symbol(tree, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 10, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 7, 41)) + + get bad() { +>bad : Symbol(bad, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 10, 21)) + + return [tree]; +>tree : Symbol(tree, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 10, 5)) + + }, +}); +export const out = tree.out; +>out : Symbol(out, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 15, 12)) +>tree.out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 4, 21)) +>tree : Symbol(tree, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 10, 5)) +>out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, 4, 21)) + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.types new file mode 100644 index 0000000000000..c8c9fc26eb17b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.types @@ -0,0 +1,40 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts === +// Skipping a member that genuinely cannot be worked out yet postpones the constraint check; it does +// not waive it. The getter names the declaration being resolved, so the skip is right, but what it +// returns still violates the constraint and that has to surface once the declaration has a type. + +interface Schema { + readonly out: O; +>out : O +} +type Shape = Record>; +>Shape : Shape + +declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>shape : S +>shape : S + +const tree = object({ +>tree : Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; } +>object({ get bad() { return [tree]; },}) : Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; } +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>{ get bad() { return [tree]; },} : { readonly bad: (Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; })[]; } + + get bad() { +>bad : (Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; })[] + + return [tree]; +>[tree] : (Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; })[] +>tree : Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; } + + }, +}); +export const out = tree.out; +>out : { readonly bad: unknown; } +>tree.out : { readonly bad: unknown; } +>tree : Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & any)[]; }; } +>out : { readonly bad: unknown; } + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.symbols new file mode 100644 index 0000000000000..a075c8b405b91 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.symbols @@ -0,0 +1,179 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnion.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterUnion.ts === +// Recursion that routes through a union, with the recursive member left required. main fails outright +// here, with an implicit-any on every declaration involved. The companion file +// recursiveTypeThroughObjectLiteralGetterUnionOptional.ts is the same shape with the member optional. +// +// The .types baseline prints the recursive member as `any` once it is a few levels deep. That is the +// type printer truncating, not the type -- the member accesses at the bottom are what pin the real +// shape, and the @ts-expect-error is what would catch an `any` standing in for it. + +interface Internals { +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 0, 0)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 8, 20)) + + readonly out: O; +>out : Symbol(Internals.out, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 8, 38)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 8, 20)) +} +interface Schema { +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 10, 1)) + + readonly internals: Internals; +>internals : Symbol(Schema.internals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 11, 18)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 0, 0)) +} +type Out = T["internals"]["out"]; +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 13, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 14, 9)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 10, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 14, 9)) + +interface StringInternals extends Internals {} +>StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 14, 51)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 0, 0)) + +interface StringSchema extends Schema { +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 16, 54)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 10, 1)) + + readonly internals: StringInternals; +>internals : Symbol(StringSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 17, 39)) +>StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 14, 51)) +} + +type Shape = Readonly<{ [k: string]: Schema }>; +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 19, 1)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 21, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 10, 1)) + +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +>ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 21, 47)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 22, 26)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 19, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 0, 0)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 22, 64)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 22, 26)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 13, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 22, 26)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 22, 64)) + +interface ObjectSchema extends Schema { +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 22, 94)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 23, 23)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 19, 1)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 19, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 10, 1)) + + readonly internals: ObjectInternals; +>internals : Symbol(ObjectSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 23, 64)) +>ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 21, 47)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 23, 23)) +} +interface UnionInternals extends Internals> {} +>UnionInternals : Symbol(UnionInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 25, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 26, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 10, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 0, 0)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 13, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 26, 25)) + +interface UnionSchema extends Schema { +>UnionSchema : Symbol(UnionSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 26, 90)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 27, 22)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 10, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 10, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 10, 1)) + + readonly internals: UnionInternals; +>internals : Symbol(UnionSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 27, 78)) +>UnionInternals : Symbol(UnionInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 25, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 27, 22)) +} + +declare function object(shape: S): ObjectSchema; +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 29, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 31, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 19, 1)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 31, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 31, 24)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 22, 94)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 31, 24)) + +declare function union(options: T): UnionSchema; +>union : Symbol(union, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 31, 68)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 32, 23)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 10, 1)) +>options : Symbol(options, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 32, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 32, 23)) +>UnionSchema : Symbol(UnionSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 26, 90)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 32, 23)) + +declare function text(): StringSchema; +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 32, 80)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 16, 54)) + +const variantA = object({ +>variantA : Symbol(variantA, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 35, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 29, 1)) + + name: text(), +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 35, 25)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 32, 80)) + + get child() { +>child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 36, 17)) + + return tree; +>tree : Symbol(tree, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 41, 5)) + + }, +}); +const tree = union([variantA]); +>tree : Symbol(tree, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 41, 5)) +>union : Symbol(union, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 31, 68)) +>variantA : Symbol(variantA, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 35, 5)) + +export type TreeOut = Out; +>TreeOut : Symbol(TreeOut, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 41, 31)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 13, 1)) +>tree : Symbol(tree, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 41, 5)) + +declare const sample: TreeOut; +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 45, 13)) +>TreeOut : Symbol(TreeOut, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 41, 31)) + +// The recursive member resolves: three levels in, `name` is still a string. +const topName: string = sample.name; +>topName : Symbol(topName, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 47, 5)) +>sample.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 35, 25)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 45, 13)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 35, 25)) + +const nestedName: string = sample.child.name; +>nestedName : Symbol(nestedName, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 48, 5)) +>sample.child.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 35, 25)) +>sample.child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 36, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 45, 13)) +>child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 36, 17)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 35, 25)) + +const deepName: string = sample.child.child.name; +>deepName : Symbol(deepName, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 49, 5)) +>sample.child.child.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 35, 25)) +>sample.child.child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 36, 17)) +>sample.child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 36, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 45, 13)) +>child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 36, 17)) +>child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 36, 17)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 35, 25)) + +// An `any` here would swallow this, so the expected error is what proves the member is a real object. +// @ts-expect-error +sample.child.missing; +>sample.child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 36, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 45, 13)) +>child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 36, 17)) + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.types new file mode 100644 index 0000000000000..6e898c835cb48 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.types @@ -0,0 +1,121 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnion.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterUnion.ts === +// Recursion that routes through a union, with the recursive member left required. main fails outright +// here, with an implicit-any on every declaration involved. The companion file +// recursiveTypeThroughObjectLiteralGetterUnionOptional.ts is the same shape with the member optional. +// +// The .types baseline prints the recursive member as `any` once it is a few levels deep. That is the +// type printer truncating, not the type -- the member accesses at the bottom are what pin the real +// shape, and the @ts-expect-error is what would catch an `any` standing in for it. + +interface Internals { + readonly out: O; +>out : O +} +interface Schema { + readonly internals: Internals; +>internals : Internals +} +type Out = T["internals"]["out"]; +>Out : Out + +interface StringInternals extends Internals {} +interface StringSchema extends Schema { + readonly internals: StringInternals; +>internals : StringInternals +} + +type Shape = Readonly<{ [k: string]: Schema }>; +>Shape : Readonly<{ [k: string]: Schema; }> +>k : string + +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +interface ObjectSchema extends Schema { + readonly internals: ObjectInternals; +>internals : ObjectInternals +} +interface UnionInternals extends Internals> {} +interface UnionSchema extends Schema { + readonly internals: UnionInternals; +>internals : UnionInternals +} + +declare function object(shape: S): ObjectSchema; +>object : (shape: S) => ObjectSchema +>shape : S + +declare function union(options: T): UnionSchema; +>union : (options: T) => UnionSchema +>options : T + +declare function text(): StringSchema; +>text : () => StringSchema + +const variantA = object({ +>variantA : ObjectSchema<{ name: StringSchema; readonly child: UnionSchema[]>; }> +>object({ name: text(), get child() { return tree; },}) : ObjectSchema<{ name: StringSchema; readonly child: UnionSchema[]>; }> +>object : (shape: S) => ObjectSchema +>{ name: text(), get child() { return tree; },} : { name: StringSchema; readonly child: UnionSchema[]>; }>[]>; } + + name: text(), +>name : StringSchema +>text() : StringSchema +>text : () => StringSchema + + get child() { +>child : UnionSchema[]>; }>[]> + + return tree; +>tree : UnionSchema[]>; }>[]> + + }, +}); +const tree = union([variantA]); +>tree : UnionSchema[]>; }>[]> +>union([variantA]) : UnionSchema[]>; }>[]> +>union : (options: T) => UnionSchema +>[variantA] : ObjectSchema<{ name: StringSchema; readonly child: UnionSchema[]>; }>[] +>variantA : ObjectSchema<{ name: StringSchema; readonly child: UnionSchema[]>; }> + +export type TreeOut = Out; +>TreeOut : { name: string; readonly child: any; } +>tree : UnionSchema[]>; }>[]> + +declare const sample: TreeOut; +>sample : { name: string; readonly child: any; } + +// The recursive member resolves: three levels in, `name` is still a string. +const topName: string = sample.name; +>topName : string +>sample.name : string +>sample : { name: string; readonly child: any; } +>name : string + +const nestedName: string = sample.child.name; +>nestedName : string +>sample.child.name : string +>sample.child : { name: string; readonly child: any; } +>sample : { name: string; readonly child: any; } +>child : { name: string; readonly child: any; } +>name : string + +const deepName: string = sample.child.child.name; +>deepName : string +>sample.child.child.name : string +>sample.child.child : { name: string; readonly child: any; } +>sample.child : { name: string; readonly child: any; } +>sample : { name: string; readonly child: any; } +>child : { name: string; readonly child: any; } +>child : { name: string; readonly child: any; } +>name : string + +// An `any` here would swallow this, so the expected error is what proves the member is a real object. +// @ts-expect-error +sample.child.missing; +>sample.child.missing : error +>sample.child : { name: string; readonly child: any; } +>sample : { name: string; readonly child: any; } +>child : { name: string; readonly child: any; } +>missing : any + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.symbols new file mode 100644 index 0000000000000..65d323228a4cd --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.symbols @@ -0,0 +1,191 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterUnionOptional.ts === +// Recursion through a union, with the recursive member optional. This is the companion to +// recursiveTypeThroughObjectLiteralGetterUnion.ts, which is the same shape with the member left +// required. main fails on both, with an implicit-any on every declaration involved. + +interface Internals { +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 0, 0)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 4, 20)) + + readonly out: O; +>out : Symbol(Internals.out, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 4, 38)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 4, 20)) +} +interface Schema { +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) + + readonly internals: Internals; +>internals : Symbol(Schema.internals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 7, 18)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 0, 0)) +} +type Out = T["internals"]["out"]; +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 9, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 10, 9)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 10, 9)) + +interface OptionalInternals extends Internals | undefined> {} +>OptionalInternals : Symbol(OptionalInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 10, 51)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 12, 28)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 0, 0)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 9, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 12, 28)) + +interface OptionalSchema extends Schema { +>OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 12, 86)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 13, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) + + readonly internals: OptionalInternals; +>internals : Symbol(OptionalSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 13, 68)) +>OptionalInternals : Symbol(OptionalInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 10, 51)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 13, 25)) +} +interface StringInternals extends Internals {} +>StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 15, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 0, 0)) + +interface StringSchema extends Schema { +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 16, 54)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) + + readonly internals: StringInternals; +>internals : Symbol(StringSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 17, 39)) +>StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 15, 1)) +} + +type Shape = Readonly<{ [k: string]: Schema }>; +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 19, 1)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 21, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) + +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +>ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 21, 47)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 22, 26)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 19, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 0, 0)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 22, 64)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 22, 26)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 9, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 22, 26)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 22, 64)) + +interface ObjectSchema extends Schema { +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 22, 94)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 23, 23)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 19, 1)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 19, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) + + readonly internals: ObjectInternals; +>internals : Symbol(ObjectSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 23, 64)) +>ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 21, 47)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 23, 23)) +} +interface UnionInternals extends Internals> {} +>UnionInternals : Symbol(UnionInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 25, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 26, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 0, 0)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 9, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 26, 25)) + +interface UnionSchema extends Schema { +>UnionSchema : Symbol(UnionSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 26, 90)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 27, 22)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) + + readonly internals: UnionInternals; +>internals : Symbol(UnionSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 27, 78)) +>UnionInternals : Symbol(UnionInternals, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 25, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 27, 22)) +} + +declare function object(shape: S): ObjectSchema; +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 29, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 31, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 19, 1)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 31, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 31, 24)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 22, 94)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 31, 24)) + +declare function union(options: T): UnionSchema; +>union : Symbol(union, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 31, 68)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 32, 23)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) +>options : Symbol(options, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 32, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 32, 23)) +>UnionSchema : Symbol(UnionSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 26, 90)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 32, 23)) + +declare function optional(t: T): OptionalSchema; +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 32, 80)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 33, 26)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 6, 1)) +>t : Symbol(t, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 33, 44)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 33, 26)) +>OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 12, 86)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 33, 26)) + +declare function text(): StringSchema; +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 33, 69)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 16, 54)) + +const variantA = object({ +>variantA : Symbol(variantA, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 36, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 29, 1)) + + name: text(), +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 36, 25)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 33, 69)) + + get child() { +>child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 37, 17)) + + return optional(tree); +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 32, 80)) +>tree : Symbol(tree, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 42, 5)) + + }, +}); +const tree = union([variantA]); +>tree : Symbol(tree, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 42, 5)) +>union : Symbol(union, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 31, 68)) +>variantA : Symbol(variantA, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 36, 5)) + +declare const sample: Out; +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 44, 13)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 9, 1)) +>tree : Symbol(tree, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 42, 5)) + +// The recursive member resolves: two levels in, `name` is still a string. +const topName: string = sample.name; +>topName : Symbol(topName, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 46, 5)) +>sample.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 36, 25)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 44, 13)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 36, 25)) + +const nestedName: string | undefined = sample.child?.name; +>nestedName : Symbol(nestedName, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 47, 5)) +>sample.child?.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 36, 25)) +>sample.child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 37, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 44, 13)) +>child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 37, 17)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 36, 25)) + +// An `any` here would swallow this, so the expected error is what proves the member is a real object. +// @ts-expect-error +sample.child?.missing; +>sample.child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 37, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 44, 13)) +>child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 37, 17)) + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.types new file mode 100644 index 0000000000000..ba18bc2175d56 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.types @@ -0,0 +1,115 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterUnionOptional.ts === +// Recursion through a union, with the recursive member optional. This is the companion to +// recursiveTypeThroughObjectLiteralGetterUnion.ts, which is the same shape with the member left +// required. main fails on both, with an implicit-any on every declaration involved. + +interface Internals { + readonly out: O; +>out : O +} +interface Schema { + readonly internals: Internals; +>internals : Internals +} +type Out = T["internals"]["out"]; +>Out : Out + +interface OptionalInternals extends Internals | undefined> {} +interface OptionalSchema extends Schema { + readonly internals: OptionalInternals; +>internals : OptionalInternals +} +interface StringInternals extends Internals {} +interface StringSchema extends Schema { + readonly internals: StringInternals; +>internals : StringInternals +} + +type Shape = Readonly<{ [k: string]: Schema }>; +>Shape : Readonly<{ [k: string]: Schema; }> +>k : string + +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +interface ObjectSchema extends Schema { + readonly internals: ObjectInternals; +>internals : ObjectInternals +} +interface UnionInternals extends Internals> {} +interface UnionSchema extends Schema { + readonly internals: UnionInternals; +>internals : UnionInternals +} + +declare function object(shape: S): ObjectSchema; +>object : (shape: S) => ObjectSchema +>shape : S + +declare function union(options: T): UnionSchema; +>union : (options: T) => UnionSchema +>options : T + +declare function optional(t: T): OptionalSchema; +>optional : (t: T) => OptionalSchema +>t : T + +declare function text(): StringSchema; +>text : () => StringSchema + +const variantA = object({ +>variantA : ObjectSchema<{ name: StringSchema; readonly child: OptionalSchema[]>>; }> +>object({ name: text(), get child() { return optional(tree); },}) : ObjectSchema<{ name: StringSchema; readonly child: OptionalSchema[]>>; }> +>object : (shape: S) => ObjectSchema +>{ name: text(), get child() { return optional(tree); },} : { name: StringSchema; readonly child: OptionalSchema[]>>; }>[]>>; } + + name: text(), +>name : StringSchema +>text() : StringSchema +>text : () => StringSchema + + get child() { +>child : OptionalSchema[]>>; }>[]>> + + return optional(tree); +>optional(tree) : OptionalSchema[]>>; }>[]>> +>optional : (t: T) => OptionalSchema +>tree : UnionSchema[]>>; }>[]> + + }, +}); +const tree = union([variantA]); +>tree : UnionSchema[]>>; }>[]> +>union([variantA]) : UnionSchema[]>>; }>[]> +>union : (options: T) => UnionSchema +>[variantA] : ObjectSchema<{ name: StringSchema; readonly child: OptionalSchema[]>>; }>[] +>variantA : ObjectSchema<{ name: StringSchema; readonly child: OptionalSchema[]>>; }> + +declare const sample: Out; +>sample : { name: string; readonly child: any | undefined; } +>tree : UnionSchema[]>>; }>[]> + +// The recursive member resolves: two levels in, `name` is still a string. +const topName: string = sample.name; +>topName : string +>sample.name : string +>sample : { name: string; readonly child: any | undefined; } +>name : string + +const nestedName: string | undefined = sample.child?.name; +>nestedName : string | undefined +>sample.child?.name : string | undefined +>sample.child : { name: string; readonly child: any | undefined; } | undefined +>sample : { name: string; readonly child: any | undefined; } +>child : { name: string; readonly child: any | undefined; } | undefined +>name : string | undefined + +// An `any` here would swallow this, so the expected error is what proves the member is a real object. +// @ts-expect-error +sample.child?.missing; +>sample.child?.missing : error +>sample.child : { name: string; readonly child: any | undefined; } | undefined +>sample : { name: string; readonly child: any | undefined; } +>child : { name: string; readonly child: any | undefined; } | undefined +>missing : any + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.symbols new file mode 100644 index 0000000000000..62a52e0a2efd8 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.symbols @@ -0,0 +1,269 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterVariations.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterVariations.ts === +// The recursive getter written the several ways it comes up: as a class's static, nested inside +// another object literal, through an intermediate call, spelled out with an annotation, and twice +// over in one file. main reports TS7022/TS7023 on every one of these, and TS2502 on the annotated +// one. + +interface Schema { +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 0, 0)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 17)) + + readonly out: O; +>out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 21)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 17)) +} +type Shape = Record>; +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 7, 1)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 0, 0)) + +declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 8, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 9, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 7, 1)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 9, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 9, 24)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 0, 0)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 9, 62)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 9, 24)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 9, 24)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 9, 62)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 9, 95)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 9, 24)) + +declare function arr>(t: T): Schema; +>arr : Symbol(arr, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 9, 107)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 10, 21)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 0, 0)) +>t : Symbol(t, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 10, 44)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 10, 21)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 10, 21)) + +declare const leaf: Schema; +>leaf : Symbol(leaf, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 11, 13)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 0, 0)) + +class Holder { +>Holder : Symbol(Holder, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 11, 35)) + + static readonly schema = object({ +>schema : Symbol(Holder.schema, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 13, 14)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 8, 41)) + + name: leaf, +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 14, 37)) +>leaf : Symbol(leaf, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 11, 13)) + + get self() { +>self : Symbol(self, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 15, 19)) + + return Holder.schema; +>Holder.schema : Symbol(Holder.schema, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 13, 14)) +>Holder : Symbol(Holder, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 11, 35)) +>schema : Symbol(Holder.schema, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 13, 14)) + + }, + }); +} +declare const fromClass: typeof Holder.schema.out; +>fromClass : Symbol(fromClass, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 21, 13)) +>Holder.schema.out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 21)) +>Holder.schema : Symbol(Holder.schema, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 13, 14)) +>Holder : Symbol(Holder, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 11, 35)) +>schema : Symbol(Holder.schema, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 13, 14)) +>out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 21)) + +const className: string = fromClass.name; +>className : Symbol(className, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 22, 5)) +>fromClass.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 14, 37)) +>fromClass : Symbol(fromClass, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 21, 13)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 14, 37)) + +const nested = object({ +>nested : Symbol(nested, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 24, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 8, 41)) + + name: leaf, +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 24, 23)) +>leaf : Symbol(leaf, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 11, 13)) + + get inner() { +>inner : Symbol(inner, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 25, 15)) + + return object({ deeper: nested }); +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 8, 41)) +>deeper : Symbol(deeper, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 27, 23)) +>nested : Symbol(nested, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 24, 5)) + + }, +}); +declare const fromNested: typeof nested.out; +>fromNested : Symbol(fromNested, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 30, 13)) +>nested.out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 21)) +>nested : Symbol(nested, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 24, 5)) +>out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 21)) + +const nestedName: string = fromNested.inner.deeper.name; +>nestedName : Symbol(nestedName, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 31, 5)) +>fromNested.inner.deeper.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 24, 23)) +>fromNested.inner.deeper : Symbol(deeper, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 27, 23)) +>fromNested.inner : Symbol(inner, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 25, 15)) +>fromNested : Symbol(fromNested, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 30, 13)) +>inner : Symbol(inner, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 25, 15)) +>deeper : Symbol(deeper, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 27, 23)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 24, 23)) + +const viaCall = object({ +>viaCall : Symbol(viaCall, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 33, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 8, 41)) + + name: leaf, +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 33, 24)) +>leaf : Symbol(leaf, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 11, 13)) + + get items() { +>items : Symbol(items, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 34, 15)) + + return arr(viaCall); +>arr : Symbol(arr, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 9, 107)) +>viaCall : Symbol(viaCall, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 33, 5)) + + }, +}); +declare const fromCall: typeof viaCall.out; +>fromCall : Symbol(fromCall, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 39, 13)) +>viaCall.out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 21)) +>viaCall : Symbol(viaCall, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 33, 5)) +>out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 21)) + +const callName: string = fromCall.items[0].items[0].name; +>callName : Symbol(callName, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 40, 5)) +>fromCall.items[0].items[0].name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 33, 24)) +>fromCall.items[0].items : Symbol(items, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 34, 15)) +>fromCall.items : Symbol(items, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 34, 15)) +>fromCall : Symbol(fromCall, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 39, 13)) +>items : Symbol(items, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 34, 15)) +>items : Symbol(items, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 34, 15)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 33, 24)) + +const annotated = object({ +>annotated : Symbol(annotated, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 42, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 8, 41)) + + name: leaf, +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 42, 26)) +>leaf : Symbol(leaf, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 11, 13)) + + get self(): typeof annotated { +>self : Symbol(self, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 43, 15)) +>annotated : Symbol(annotated, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 42, 5)) + + return annotated; +>annotated : Symbol(annotated, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 42, 5)) + + }, +}); +declare const fromAnnotated: typeof annotated.out; +>fromAnnotated : Symbol(fromAnnotated, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 48, 13)) +>annotated.out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 21)) +>annotated : Symbol(annotated, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 42, 5)) +>out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 21)) + +const annotatedName: string = fromAnnotated.self.self.name; +>annotatedName : Symbol(annotatedName, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 49, 5)) +>fromAnnotated.self.self.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 42, 26)) +>fromAnnotated.self.self : Symbol(self, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 43, 15)) +>fromAnnotated.self : Symbol(self, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 43, 15)) +>fromAnnotated : Symbol(fromAnnotated, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 48, 13)) +>self : Symbol(self, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 43, 15)) +>self : Symbol(self, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 43, 15)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 42, 26)) + +// Two independent recursive schemas must not contaminate each other. +const first = object({ a: leaf, get me() { return first; } }); +>first : Symbol(first, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 52, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 8, 41)) +>a : Symbol(a, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 52, 22)) +>leaf : Symbol(leaf, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 11, 13)) +>me : Symbol(me, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 52, 31)) +>first : Symbol(first, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 52, 5)) + +const second = object({ b: leaf, get me() { return second; } }); +>second : Symbol(second, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 53, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 8, 41)) +>b : Symbol(b, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 53, 23)) +>leaf : Symbol(leaf, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 11, 13)) +>me : Symbol(me, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 53, 32)) +>second : Symbol(second, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 53, 5)) + +declare const fromFirst: typeof first.out; +>fromFirst : Symbol(fromFirst, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 54, 13)) +>first.out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 21)) +>first : Symbol(first, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 52, 5)) +>out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 21)) + +declare const fromSecond: typeof second.out; +>fromSecond : Symbol(fromSecond, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 55, 13)) +>second.out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 21)) +>second : Symbol(second, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 53, 5)) +>out : Symbol(Schema.out, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 5, 21)) + +const firstA: string = fromFirst.me.a; +>firstA : Symbol(firstA, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 56, 5)) +>fromFirst.me.a : Symbol(a, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 52, 22)) +>fromFirst.me : Symbol(me, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 52, 31)) +>fromFirst : Symbol(fromFirst, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 54, 13)) +>me : Symbol(me, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 52, 31)) +>a : Symbol(a, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 52, 22)) + +const secondB: string = fromSecond.me.b; +>secondB : Symbol(secondB, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 57, 5)) +>fromSecond.me.b : Symbol(b, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 53, 23)) +>fromSecond.me : Symbol(me, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 53, 32)) +>fromSecond : Symbol(fromSecond, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 55, 13)) +>me : Symbol(me, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 53, 32)) +>b : Symbol(b, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 53, 23)) + +// Every assertion above still passes if the type degraded to `any`. These do not: each names a key +// the schema does not have, so the expected error is what shows the member is a real object type. +// @ts-expect-error +fromClass.self.missing; +>fromClass.self : Symbol(self, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 15, 19)) +>fromClass : Symbol(fromClass, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 21, 13)) +>self : Symbol(self, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 15, 19)) + +// @ts-expect-error +fromNested.inner.deeper.missing; +>fromNested.inner.deeper : Symbol(deeper, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 27, 23)) +>fromNested.inner : Symbol(inner, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 25, 15)) +>fromNested : Symbol(fromNested, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 30, 13)) +>inner : Symbol(inner, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 25, 15)) +>deeper : Symbol(deeper, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 27, 23)) + +// @ts-expect-error +fromCall.items[0].missing; +>fromCall.items : Symbol(items, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 34, 15)) +>fromCall : Symbol(fromCall, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 39, 13)) +>items : Symbol(items, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 34, 15)) + +// @ts-expect-error +fromAnnotated.self.missing; +>fromAnnotated.self : Symbol(self, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 43, 15)) +>fromAnnotated : Symbol(fromAnnotated, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 48, 13)) +>self : Symbol(self, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 43, 15)) + +// @ts-expect-error +fromFirst.me.missing; +>fromFirst.me : Symbol(me, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 52, 31)) +>fromFirst : Symbol(fromFirst, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 54, 13)) +>me : Symbol(me, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 52, 31)) + +// @ts-expect-error +fromSecond.me.missing; +>fromSecond.me : Symbol(me, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 53, 32)) +>fromSecond : Symbol(fromSecond, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 55, 13)) +>me : Symbol(me, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 53, 32)) + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.types new file mode 100644 index 0000000000000..8a78711b3c951 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.types @@ -0,0 +1,281 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterVariations.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterVariations.ts === +// The recursive getter written the several ways it comes up: as a class's static, nested inside +// another object literal, through an intermediate call, spelled out with an annotation, and twice +// over in one file. main reports TS7022/TS7023 on every one of these, and TS2502 on the annotated +// one. + +interface Schema { + readonly out: O; +>out : O +} +type Shape = Record>; +>Shape : Shape + +declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>shape : S +>shape : S + +declare function arr>(t: T): Schema; +>arr : >(t: T) => Schema +>t : T + +declare const leaf: Schema; +>leaf : Schema + +class Holder { +>Holder : Holder + + static readonly schema = object({ +>schema : Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: Schema<{ name: string; readonly self: any; }> & any; }; } +>object({ name: leaf, get self() { return Holder.schema; }, }) : Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: Schema<{ name: string; readonly self: any; }> & any; }; } +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>{ name: leaf, get self() { return Holder.schema; }, } : { name: Schema; readonly self: Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: Schema<{ name: string; readonly self: any; }> & any; }; }; } + + name: leaf, +>name : Schema +>leaf : Schema + + get self() { +>self : Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: Schema<{ name: string; readonly self: any; }> & any; }; } + + return Holder.schema; +>Holder.schema : Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: Schema<{ name: string; readonly self: any; }> & any; }; } +>Holder : typeof Holder +>schema : Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: Schema<{ name: string; readonly self: any; }> & any; }; } + + }, + }); +} +declare const fromClass: typeof Holder.schema.out; +>fromClass : { name: string; readonly self: any; } +>Holder.schema.out : { name: string; readonly self: any; } +>Holder.schema : Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: Schema<{ name: string; readonly self: any; }> & any; }; } +>Holder : typeof Holder +>schema : Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: Schema<{ name: string; readonly self: any; }> & any; }; } +>out : { name: string; readonly self: any; } + +const className: string = fromClass.name; +>className : string +>fromClass.name : string +>fromClass : { name: string; readonly self: any; } +>name : string + +const nested = object({ +>nested : Schema<{ name: string; readonly inner: { deeper: any; }; }> & { shape: { name: Schema; readonly inner: Schema<{ deeper: any; }> & { shape: { deeper: Schema<{ name: string; readonly inner: { deeper: any; }; }> & any; }; }; }; } +>object({ name: leaf, get inner() { return object({ deeper: nested }); },}) : Schema<{ name: string; readonly inner: { deeper: any; }; }> & { shape: { name: Schema; readonly inner: Schema<{ deeper: any; }> & { shape: { deeper: Schema<{ name: string; readonly inner: { deeper: any; }; }> & any; }; }; }; } +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>{ name: leaf, get inner() { return object({ deeper: nested }); },} : { name: Schema; readonly inner: Schema<{ deeper: { name: string; readonly inner: any; }; }> & { shape: { deeper: Schema<{ name: string; readonly inner: any; }> & { shape: { name: Schema; readonly inner: Schema<{ deeper: { name: string; readonly inner: any; }; }> & any; }; }; }; }; } + + name: leaf, +>name : Schema +>leaf : Schema + + get inner() { +>inner : Schema<{ deeper: { name: string; readonly inner: any; }; }> & { shape: { deeper: Schema<{ name: string; readonly inner: any; }> & { shape: { name: Schema; readonly inner: Schema<{ deeper: { name: string; readonly inner: any; }; }> & any; }; }; }; } + + return object({ deeper: nested }); +>object({ deeper: nested }) : Schema<{ deeper: { name: string; readonly inner: any; }; }> & { shape: { deeper: Schema<{ name: string; readonly inner: any; }> & { shape: { name: Schema; readonly inner: Schema<{ deeper: { name: string; readonly inner: any; }; }> & any; }; }; }; } +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>{ deeper: nested } : { deeper: Schema<{ name: string; readonly inner: { deeper: any; }; }> & { shape: { name: Schema; readonly inner: Schema<{ deeper: any; }> & { shape: { deeper: Schema<{ name: string; readonly inner: { deeper: any; }; }> & any; }; }; }; }; } +>deeper : Schema<{ name: string; readonly inner: { deeper: any; }; }> & { shape: { name: Schema; readonly inner: Schema<{ deeper: any; }> & { shape: { deeper: Schema<{ name: string; readonly inner: { deeper: any; }; }> & any; }; }; }; } +>nested : Schema<{ name: string; readonly inner: { deeper: any; }; }> & { shape: { name: Schema; readonly inner: Schema<{ deeper: any; }> & { shape: { deeper: Schema<{ name: string; readonly inner: { deeper: any; }; }> & any; }; }; }; } + + }, +}); +declare const fromNested: typeof nested.out; +>fromNested : { name: string; readonly inner: { deeper: any; }; } +>nested.out : { name: string; readonly inner: { deeper: any; }; } +>nested : Schema<{ name: string; readonly inner: { deeper: any; }; }> & { shape: { name: Schema; readonly inner: Schema<{ deeper: any; }> & { shape: { deeper: Schema<{ name: string; readonly inner: { deeper: any; }; }> & any; }; }; }; } +>out : { name: string; readonly inner: { deeper: any; }; } + +const nestedName: string = fromNested.inner.deeper.name; +>nestedName : string +>fromNested.inner.deeper.name : string +>fromNested.inner.deeper : { name: string; readonly inner: { deeper: any; }; } +>fromNested.inner : { deeper: { name: string; readonly inner: any; }; } +>fromNested : { name: string; readonly inner: { deeper: any; }; } +>inner : { deeper: { name: string; readonly inner: any; }; } +>deeper : { name: string; readonly inner: { deeper: any; }; } +>name : string + +const viaCall = object({ +>viaCall : Schema<{ name: string; readonly items: any[]; }> & { shape: { name: Schema; readonly items: Schema<{ name: string; readonly items: any[]; }[]>; }; } +>object({ name: leaf, get items() { return arr(viaCall); },}) : Schema<{ name: string; readonly items: any[]; }> & { shape: { name: Schema; readonly items: Schema<{ name: string; readonly items: any[]; }[]>; }; } +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>{ name: leaf, get items() { return arr(viaCall); },} : { name: Schema; readonly items: Schema<{ name: string; readonly items: any[]; }[]>; } + + name: leaf, +>name : Schema +>leaf : Schema + + get items() { +>items : Schema<{ name: string; readonly items: any[]; }[]> + + return arr(viaCall); +>arr(viaCall) : Schema<{ name: string; readonly items: any[]; }[]> +>arr : >(t: T) => Schema +>viaCall : Schema<{ name: string; readonly items: any[]; }> & { shape: { name: Schema; readonly items: Schema<{ name: string; readonly items: any[]; }[]>; }; } + + }, +}); +declare const fromCall: typeof viaCall.out; +>fromCall : { name: string; readonly items: any[]; } +>viaCall.out : { name: string; readonly items: any[]; } +>viaCall : Schema<{ name: string; readonly items: any[]; }> & { shape: { name: Schema; readonly items: Schema<{ name: string; readonly items: any[]; }[]>; }; } +>out : { name: string; readonly items: any[]; } + +const callName: string = fromCall.items[0].items[0].name; +>callName : string +>fromCall.items[0].items[0].name : string +>fromCall.items[0].items[0] : { name: string; readonly items: any[]; } +>fromCall.items[0].items : { name: string; readonly items: any[]; }[] +>fromCall.items[0] : { name: string; readonly items: any[]; } +>fromCall.items : { name: string; readonly items: any[]; }[] +>fromCall : { name: string; readonly items: any[]; } +>items : { name: string; readonly items: any[]; }[] +>0 : 0 +>items : { name: string; readonly items: any[]; }[] +>0 : 0 +>name : string + +const annotated = object({ +>annotated : Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: typeof annotated; }; } +>object({ name: leaf, get self(): typeof annotated { return annotated; },}) : Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: typeof annotated; }; } +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>{ name: leaf, get self(): typeof annotated { return annotated; },} : { name: Schema; readonly self: typeof annotated; } + + name: leaf, +>name : Schema +>leaf : Schema + + get self(): typeof annotated { +>self : Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: typeof annotated; }; } +>annotated : Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: typeof annotated; }; } + + return annotated; +>annotated : Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: typeof annotated; }; } + + }, +}); +declare const fromAnnotated: typeof annotated.out; +>fromAnnotated : { name: string; readonly self: any; } +>annotated.out : { name: string; readonly self: any; } +>annotated : Schema<{ name: string; readonly self: any; }> & { shape: { name: Schema; readonly self: typeof annotated; }; } +>out : { name: string; readonly self: any; } + +const annotatedName: string = fromAnnotated.self.self.name; +>annotatedName : string +>fromAnnotated.self.self.name : string +>fromAnnotated.self.self : { name: string; readonly self: any; } +>fromAnnotated.self : { name: string; readonly self: any; } +>fromAnnotated : { name: string; readonly self: any; } +>self : { name: string; readonly self: any; } +>self : { name: string; readonly self: any; } +>name : string + +// Two independent recursive schemas must not contaminate each other. +const first = object({ a: leaf, get me() { return first; } }); +>first : Schema<{ a: string; readonly me: any; }> & { shape: { a: Schema; readonly me: Schema<{ a: string; readonly me: any; }> & any; }; } +>object({ a: leaf, get me() { return first; } }) : Schema<{ a: string; readonly me: any; }> & { shape: { a: Schema; readonly me: Schema<{ a: string; readonly me: any; }> & any; }; } +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>{ a: leaf, get me() { return first; } } : { a: Schema; readonly me: Schema<{ a: string; readonly me: any; }> & { shape: { a: Schema; readonly me: Schema<{ a: string; readonly me: any; }> & any; }; }; } +>a : Schema +>leaf : Schema +>me : Schema<{ a: string; readonly me: any; }> & { shape: { a: Schema; readonly me: Schema<{ a: string; readonly me: any; }> & any; }; } +>first : Schema<{ a: string; readonly me: any; }> & { shape: { a: Schema; readonly me: Schema<{ a: string; readonly me: any; }> & any; }; } + +const second = object({ b: leaf, get me() { return second; } }); +>second : Schema<{ b: string; readonly me: any; }> & { shape: { b: Schema; readonly me: Schema<{ b: string; readonly me: any; }> & any; }; } +>object({ b: leaf, get me() { return second; } }) : Schema<{ b: string; readonly me: any; }> & { shape: { b: Schema; readonly me: Schema<{ b: string; readonly me: any; }> & any; }; } +>object : (shape: S) => Schema<{ [K in keyof S]: S[K]["out"]; }> & { shape: S; } +>{ b: leaf, get me() { return second; } } : { b: Schema; readonly me: Schema<{ b: string; readonly me: any; }> & { shape: { b: Schema; readonly me: Schema<{ b: string; readonly me: any; }> & any; }; }; } +>b : Schema +>leaf : Schema +>me : Schema<{ b: string; readonly me: any; }> & { shape: { b: Schema; readonly me: Schema<{ b: string; readonly me: any; }> & any; }; } +>second : Schema<{ b: string; readonly me: any; }> & { shape: { b: Schema; readonly me: Schema<{ b: string; readonly me: any; }> & any; }; } + +declare const fromFirst: typeof first.out; +>fromFirst : { a: string; readonly me: any; } +>first.out : { a: string; readonly me: any; } +>first : Schema<{ a: string; readonly me: any; }> & { shape: { a: Schema; readonly me: Schema<{ a: string; readonly me: any; }> & any; }; } +>out : { a: string; readonly me: any; } + +declare const fromSecond: typeof second.out; +>fromSecond : { b: string; readonly me: any; } +>second.out : { b: string; readonly me: any; } +>second : Schema<{ b: string; readonly me: any; }> & { shape: { b: Schema; readonly me: Schema<{ b: string; readonly me: any; }> & any; }; } +>out : { b: string; readonly me: any; } + +const firstA: string = fromFirst.me.a; +>firstA : string +>fromFirst.me.a : string +>fromFirst.me : { a: string; readonly me: any; } +>fromFirst : { a: string; readonly me: any; } +>me : { a: string; readonly me: any; } +>a : string + +const secondB: string = fromSecond.me.b; +>secondB : string +>fromSecond.me.b : string +>fromSecond.me : { b: string; readonly me: any; } +>fromSecond : { b: string; readonly me: any; } +>me : { b: string; readonly me: any; } +>b : string + +// Every assertion above still passes if the type degraded to `any`. These do not: each names a key +// the schema does not have, so the expected error is what shows the member is a real object type. +// @ts-expect-error +fromClass.self.missing; +>fromClass.self.missing : error +>fromClass.self : { name: string; readonly self: any; } +>fromClass : { name: string; readonly self: any; } +>self : { name: string; readonly self: any; } +>missing : any + +// @ts-expect-error +fromNested.inner.deeper.missing; +>fromNested.inner.deeper.missing : error +>fromNested.inner.deeper : { name: string; readonly inner: { deeper: any; }; } +>fromNested.inner : { deeper: { name: string; readonly inner: any; }; } +>fromNested : { name: string; readonly inner: { deeper: any; }; } +>inner : { deeper: { name: string; readonly inner: any; }; } +>deeper : { name: string; readonly inner: { deeper: any; }; } +>missing : any + +// @ts-expect-error +fromCall.items[0].missing; +>fromCall.items[0].missing : error +>fromCall.items[0] : { name: string; readonly items: any[]; } +>fromCall.items : { name: string; readonly items: any[]; }[] +>fromCall : { name: string; readonly items: any[]; } +>items : { name: string; readonly items: any[]; }[] +>0 : 0 +>missing : any + +// @ts-expect-error +fromAnnotated.self.missing; +>fromAnnotated.self.missing : error +>fromAnnotated.self : { name: string; readonly self: any; } +>fromAnnotated : { name: string; readonly self: any; } +>self : { name: string; readonly self: any; } +>missing : any + +// @ts-expect-error +fromFirst.me.missing; +>fromFirst.me.missing : error +>fromFirst.me : { a: string; readonly me: any; } +>fromFirst : { a: string; readonly me: any; } +>me : { a: string; readonly me: any; } +>missing : any + +// @ts-expect-error +fromSecond.me.missing; +>fromSecond.me.missing : error +>fromSecond.me : { b: string; readonly me: any; } +>fromSecond : { b: string; readonly me: any; } +>me : { b: string; readonly me: any; } +>missing : any + diff --git a/tsc/testdata/baselines/reference/fourslash/syntaxandSemanticDiagnostics/postponedConstraintDiagnosticsConsumerFirst.baseline b/tsc/testdata/baselines/reference/fourslash/syntaxandSemanticDiagnostics/postponedConstraintDiagnosticsConsumerFirst.baseline new file mode 100644 index 0000000000000..d1a90112f39dc --- /dev/null +++ b/tsc/testdata/baselines/reference/fourslash/syntaxandSemanticDiagnostics/postponedConstraintDiagnosticsConsumerFirst.baseline @@ -0,0 +1,23 @@ +// === Syntax and Semantic Diagnostics === +/schema.ts(8,9): error TS2741: Property 'out' is missing in type '(Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & ...)[]; }; })[]' but required in type 'Schema'. + + +==== /consumer.ts (0 errors) ==== + import { tree } from "./schema.js"; + + export const out = tree.out; +==== /schema.ts (1 errors) ==== + export interface Schema { + readonly out: O; + } + export type Shape = Record>; + export declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; + + export const tree = object({ + get bad() { + ~~~ +!!! error TS2741: Property 'out' is missing in type '(Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & ...)[]; }; })[]' but required in type 'Schema'. +!!! related TS2741 /schema.ts:2:14: 'out' is declared here. + return [tree]; + }, + }); \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/fourslash/syntaxandSemanticDiagnostics/postponedConstraintDiagnosticsConsumerOnly.baseline b/tsc/testdata/baselines/reference/fourslash/syntaxandSemanticDiagnostics/postponedConstraintDiagnosticsConsumerOnly.baseline new file mode 100644 index 0000000000000..d1a90112f39dc --- /dev/null +++ b/tsc/testdata/baselines/reference/fourslash/syntaxandSemanticDiagnostics/postponedConstraintDiagnosticsConsumerOnly.baseline @@ -0,0 +1,23 @@ +// === Syntax and Semantic Diagnostics === +/schema.ts(8,9): error TS2741: Property 'out' is missing in type '(Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & ...)[]; }; })[]' but required in type 'Schema'. + + +==== /consumer.ts (0 errors) ==== + import { tree } from "./schema.js"; + + export const out = tree.out; +==== /schema.ts (1 errors) ==== + export interface Schema { + readonly out: O; + } + export type Shape = Record>; + export declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; + + export const tree = object({ + get bad() { + ~~~ +!!! error TS2741: Property 'out' is missing in type '(Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & ...)[]; }; })[]' but required in type 'Schema'. +!!! related TS2741 /schema.ts:2:14: 'out' is declared here. + return [tree]; + }, + }); \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/fourslash/syntaxandSemanticDiagnostics/postponedConstraintDiagnosticsSchemaFirst.baseline b/tsc/testdata/baselines/reference/fourslash/syntaxandSemanticDiagnostics/postponedConstraintDiagnosticsSchemaFirst.baseline new file mode 100644 index 0000000000000..d1a90112f39dc --- /dev/null +++ b/tsc/testdata/baselines/reference/fourslash/syntaxandSemanticDiagnostics/postponedConstraintDiagnosticsSchemaFirst.baseline @@ -0,0 +1,23 @@ +// === Syntax and Semantic Diagnostics === +/schema.ts(8,9): error TS2741: Property 'out' is missing in type '(Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & ...)[]; }; })[]' but required in type 'Schema'. + + +==== /consumer.ts (0 errors) ==== + import { tree } from "./schema.js"; + + export const out = tree.out; +==== /schema.ts (1 errors) ==== + export interface Schema { + readonly out: O; + } + export type Shape = Record>; + export declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; + + export const tree = object({ + get bad() { + ~~~ +!!! error TS2741: Property 'out' is missing in type '(Schema<{ readonly bad: unknown; }> & { shape: { readonly bad: (Schema<{ readonly bad: unknown; }> & ...)[]; }; })[]' but required in type 'Schema'. +!!! related TS2741 /schema.ts:2:14: 'out' is declared here. + return [tree]; + }, + }); \ No newline at end of file diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetter.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetter.ts new file mode 100644 index 0000000000000..45743d0ca6167 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetter.ts @@ -0,0 +1,55 @@ +// @strict: true +// @noEmit: true + +// A getter is how a self-referential object literal is written, and its type is worked out lazily. +// Inferring the type argument for `object` must not force that getter, which reports a circularity +// and fixes the member at `any`. Reduced from #62180 and #62181. + +interface Internals { + readonly out: O; +} +interface Schema { + readonly internals: Internals; +} +type Out = T["internals"]["out"]; + +interface ArrayInternals extends Internals[]> {} +interface ArraySchema extends Schema { + readonly internals: ArrayInternals; +} +interface StringInternals extends Internals {} +interface StringSchema extends Schema { + readonly internals: StringInternals; +} + +type Shape = Readonly<{ [k: string]: Schema }>; +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +interface ObjectSchema extends Schema { + readonly internals: ObjectInternals; +} + +declare function object(shape: S): ObjectSchema; +declare function array(element: T): ArraySchema; +declare function text(): StringSchema; + +const node = object({ + name: text(), + get children() { + return array(node); + }, +}); + +type TreeNode = Out; +declare const sample: TreeNode; + +// The recursive member is an array of the same node type, not `any` and not `unknown`. +const leafName: string = sample.name; +const nestedName: string = sample.children[0].children[0].name; + +// Those two pass on their own if the whole thing collapsed to `any`, which is exactly the failure +// this test exists to catch, so the recursion point is also probed where `any` cannot hide: an +// unknown key has to be an error, and a known one has to have the type it is declared with. +// @ts-expect-error +sample.children[0].missing; +// @ts-expect-error +const wrongLeafType: number = sample.children[0].name; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts new file mode 100644 index 0000000000000..0d628bc094406 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts @@ -0,0 +1,34 @@ +// @strict: true +// @noEmit: true + +// The companion to recursiveTypeThroughObjectLiteralGetterAbsentProperty, on the speculative path. +// Inferring `T` from `Wrapper` opens a region, and checking the inferred type against the +// constraint reads `D` while its base is still being instantiated. A conditional asked about `D` in +// that window used to be told every property matched, so `[D] extends [Need]` took the true branch +// and the index signature became `{ run(): void }` -- `d.anything.run()` then went unreported. +// +// The window withholds inherited members and nothing else, so `missing` -- which no base of `D` +// declares -- is genuinely absent even there. The conditional has to take the same branch it would +// take once the table is complete, and the call has to stay an error. +// +// The opposite direction -- a name a base does declare, which has to stay withheld -- is guarded by +// the two hover tests for #62181 and by the mapped-type case, all three of which fail if the miss is +// reported instead. A case written here for it would not reach the skip at all. + +export function run(): void { + force(w); +} + +export function probe(): void { + d.anything.run(); +} + +interface Need { missing: string } +type Guarded = [T] extends [Need] ? { run(): void } : string; +interface Base { [k: string]: Guarded; } +interface D extends Base { own: never; } +interface Wrapper { readonly w: T } +declare function force(x: Wrapper): T; +declare const w: Wrapper; +declare const d: D; + diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts new file mode 100644 index 0000000000000..5fe376b8b81a3 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentProperty.ts @@ -0,0 +1,23 @@ +// @strict: true +// @noEmit: true + +// A property that is genuinely absent has to stay absent. While ObjectFlagsUnresolvedMembers is set +// the member table holds only what the type declares itself, so a miss in that window is treated as +// "not known yet" -- but only inside a speculative region, where the answer is provisional anyway. +// +// Without that restriction this compiles clean: `D` never has `missing`, so the conditional has to +// pick `string` and `.run()` on it is an error. Suppressing the unmatched property during an ordinary +// check makes the conditional take the wrong branch and the call goes unreported. + +interface Need { + missing: string; +} +type Guarded = [T] extends [Need] ? { run(): void } : string; +interface Base { + [k: string]: Guarded; +} +interface D extends Base { + own: never; +} +declare const d: D; +d.anything.run(); diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.ts new file mode 100644 index 0000000000000..d03d6387de600 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.ts @@ -0,0 +1,22 @@ +// @strict: true +// @noEmit: true + +// Skipping an accessor during a check that reports nothing is a deferral, not a waiver. An array of +// schemas is not a Schema, so this reports. The baseline is byte-identical to what an unpatched +// compiler produces, and that is the whole point of the case: it pins that the constraint is still +// enforced, not which pass enforced it. The recursive shape where the deferred pass is the only thing +// that can report it is recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts. + +interface Schema { + readonly out: O; +} +type Shape = Record>; +declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; +declare const leaf: Schema; + +const flat = object({ + get bad() { + return [leaf]; + }, +}); +export const out = flat.out; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.ts new file mode 100644 index 0000000000000..5ade0a5a8e4f5 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterCrossFile.ts @@ -0,0 +1,27 @@ +// @strict: true +// @noEmit: true + +// The single-file case in recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts, split across +// two files. An array of schemas is not a Schema, so `bad` violates the constraint on S, and that has +// to be reported wherever the declaration and its use happen to live. Splitting them changes the +// order things resolve in: the constraint is answered while the getter still stands on an absorbed +// circularity, and a placeholder relates to anything. So the verdict is not recorded, and it is asked +// again once the file's deferred work runs. + +// @filename: schema.ts +export interface Schema { + readonly out: O; +} +export type Shape = Record>; +export declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; + +export const tree = object({ + get bad() { + return [tree]; + }, +}); + +// @filename: consumer.ts +import { tree } from "./schema.js"; + +export const out = tree.out; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts new file mode 100644 index 0000000000000..f060a04d4d9a1 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterDeclarationEmit.ts @@ -0,0 +1,43 @@ +// @strict: true +// @declaration: true +// @emitDeclarationOnly: true + +// What the recursive schema looks like coming out as a declaration. On main this is +// `export declare const node: any` plus two implicit-any errors, because the type never resolved. +// The recursion point itself still comes out as `/*elided*/ any` -- the node builder has no way to +// write a reference back into the type it is in the middle of writing, and main elides the same way +// on a type it can resolve. Everything around it is now the real shape. + +interface Internals { + readonly out: O; +} +interface Schema { + readonly internals: Internals; +} +type Out = T["internals"]["out"]; + +interface ArrayInternals extends Internals[]> {} +interface ArraySchema extends Schema { + readonly internals: ArrayInternals; +} +interface StringInternals extends Internals {} +interface StringSchema extends Schema { + readonly internals: StringInternals; +} + +type Shape = Readonly<{ [k: string]: Schema }>; +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +interface ObjectSchema extends Schema { + readonly internals: ObjectInternals; +} + +declare function object(shape: S): ObjectSchema; +declare function array(element: T): ArraySchema; +declare function text(): StringSchema; + +export const node = object({ + name: text(), + get children() { + return array(node); + }, +}); diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts new file mode 100644 index 0000000000000..398155d5947f6 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts @@ -0,0 +1,73 @@ +// @strict: true +// @noEmit: true + +// The shape from #62180, where the recursive member is reached through a mapped type that remaps its +// keys by reading each member's own discriminant. Resolving the object schema's members instantiates +// its base with a type that names the schema back, so the schema's table is read while it is still +// being assembled and its inherited `out` is not in it yet. +// +// This case is NOT fixed, and the baseline records how far it gets. An earlier draft answered every +// miss in that window with `any`, which cleared the error here -- but that answer is not confined to +// this shape. A self-referential interface reaches the same window with no getter and no inference +// anywhere in the program, and an unpatched compiler answers it from the index signature; making it +// `any` silently accepted an indexed access that every other compiler rejects. Nothing available at +// the miss distinguishes the two, so the answer was withdrawn rather than guessed. +// +// What is left is still an improvement on main, which cannot type the declaration at all: the +// getter resolves, the non-recursive members are real types, and only the remapped member is +// unresolved. + +interface Internals { + optional: "true" | "false"; + out: O; +} + +interface StringSchema extends Internals { + optional: "false"; +} + +type Shape = Record; +type Prettify = { [K in keyof T]: T[K] } & {}; +type ObjectOut = Prettify< + { + [K in keyof S as S[K] extends { optional: "true" } ? K : never]?: S[K]["out"]; + } & { + [K in keyof S as S[K] extends { optional: "true" } ? never : K]: S[K]["out"]; + } +>; + +interface ObjectSchema extends Internals> { + optional: "false"; +} + +interface OptionalSchema> extends Internals { + optional: "true"; +} + +declare function object(shape: S): ObjectSchema; +declare function text(): StringSchema; +declare function optional>(schema: T): OptionalSchema; + +const category = object({ + name: text(), + get parent() { + return optional(category); + }, +}); + +declare const sample: (typeof category)["out"]; +// The key that does not recurse resolves fully, and the remapping put the recursive one in the +// optional bucket, so it is `parent?:` rather than `parent:`. Both of those are new: main gives the +// whole declaration an implicit `any` and reports it. +const topName: string = sample.name; +// The recursive one does not resolve. Pinned so that closing this shows up here. +const parentName: string = sample.parent!.name; + +// A property that is genuinely absent must still be reported, whoever is asking. +interface Base { readonly own: number; } +interface Derived extends Base { readonly extra: string; } +declare const derived: Derived; +// @ts-expect-error +derived.missing; +const ownValue: number = derived.own; +const extraValue: string = derived.extra; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.ts new file mode 100644 index 0000000000000..b4febc8f6167a --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.ts @@ -0,0 +1,52 @@ +// @strict: true +// @noEmit: true + +// Whether a get accessor has been resolved records what has been asked for so far, not anything +// about the program, so skipping one on that alone makes an ordinary comparison depend on +// resolution order. This picked the numeric overload for an object whose only member is a string. +// +// The generic pair at the bottom matters more than the plain one: only a generic signature opens the +// speculative region where the skip is allowed at all, so the non-generic overloads never reach the +// code under test. Skipping there made the object look like it had no string member and picked the +// numeric candidate again, this time with the region wide open. + +declare function pick(x: { [k: string]: number }): "num"; +declare function pick(x: object): "obj"; + +const inferred = pick({ + get s() { + return "hello"; + }, +}); +const mustBeObj: "obj" = inferred; + +const annotated = pick({ + get s(): string { + return "hello"; + }, +}); +const alsoObj: "obj" = annotated; + +const numeric = pick({ + get n() { + return 1; + }, +}); +const mustBeNum: "num" = numeric; + +declare function generic(x: T): "num"; +declare function generic(x: T): "obj"; + +const inferredGeneric = generic({ + get s() { + return "hello"; + }, +}); +const genericMustBeObj: "obj" = inferredGeneric; + +const numericGeneric = generic({ + get n() { + return 1; + }, +}); +const genericMustBeNum: "num" = numericGeneric; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts new file mode 100644 index 0000000000000..44536b530abb4 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts @@ -0,0 +1,19 @@ +// @strict: true +// @noEmit: true + +// Skipping a member that genuinely cannot be worked out yet postpones the constraint check; it does +// not waive it. The getter names the declaration being resolved, so the skip is right, but what it +// returns still violates the constraint and that has to surface once the declaration has a type. + +interface Schema { + readonly out: O; +} +type Shape = Record>; +declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; + +const tree = object({ + get bad() { + return [tree]; + }, +}); +export const out = tree.out; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnion.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnion.ts new file mode 100644 index 0000000000000..dd1cf74223c1b --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnion.ts @@ -0,0 +1,56 @@ +// @strict: true +// @noEmit: true + +// Recursion that routes through a union, with the recursive member left required. main fails outright +// here, with an implicit-any on every declaration involved. The companion file +// recursiveTypeThroughObjectLiteralGetterUnionOptional.ts is the same shape with the member optional. +// +// The .types baseline prints the recursive member as `any` once it is a few levels deep. That is the +// type printer truncating, not the type -- the member accesses at the bottom are what pin the real +// shape, and the @ts-expect-error is what would catch an `any` standing in for it. + +interface Internals { + readonly out: O; +} +interface Schema { + readonly internals: Internals; +} +type Out = T["internals"]["out"]; + +interface StringInternals extends Internals {} +interface StringSchema extends Schema { + readonly internals: StringInternals; +} + +type Shape = Readonly<{ [k: string]: Schema }>; +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +interface ObjectSchema extends Schema { + readonly internals: ObjectInternals; +} +interface UnionInternals extends Internals> {} +interface UnionSchema extends Schema { + readonly internals: UnionInternals; +} + +declare function object(shape: S): ObjectSchema; +declare function union(options: T): UnionSchema; +declare function text(): StringSchema; + +const variantA = object({ + name: text(), + get child() { + return tree; + }, +}); +const tree = union([variantA]); + +export type TreeOut = Out; + +declare const sample: TreeOut; +// The recursive member resolves: three levels in, `name` is still a string. +const topName: string = sample.name; +const nestedName: string = sample.child.name; +const deepName: string = sample.child.child.name; +// An `any` here would swallow this, so the expected error is what proves the member is a real object. +// @ts-expect-error +sample.child.missing; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.ts new file mode 100644 index 0000000000000..117b5c7ec2c8b --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.ts @@ -0,0 +1,54 @@ +// @strict: true +// @noEmit: true + +// Recursion through a union, with the recursive member optional. This is the companion to +// recursiveTypeThroughObjectLiteralGetterUnion.ts, which is the same shape with the member left +// required. main fails on both, with an implicit-any on every declaration involved. + +interface Internals { + readonly out: O; +} +interface Schema { + readonly internals: Internals; +} +type Out = T["internals"]["out"]; + +interface OptionalInternals extends Internals | undefined> {} +interface OptionalSchema extends Schema { + readonly internals: OptionalInternals; +} +interface StringInternals extends Internals {} +interface StringSchema extends Schema { + readonly internals: StringInternals; +} + +type Shape = Readonly<{ [k: string]: Schema }>; +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +interface ObjectSchema extends Schema { + readonly internals: ObjectInternals; +} +interface UnionInternals extends Internals> {} +interface UnionSchema extends Schema { + readonly internals: UnionInternals; +} + +declare function object(shape: S): ObjectSchema; +declare function union(options: T): UnionSchema; +declare function optional(t: T): OptionalSchema; +declare function text(): StringSchema; + +const variantA = object({ + name: text(), + get child() { + return optional(tree); + }, +}); +const tree = union([variantA]); + +declare const sample: Out; +// The recursive member resolves: two levels in, `name` is still a string. +const topName: string = sample.name; +const nestedName: string | undefined = sample.child?.name; +// An `any` here would swallow this, so the expected error is what proves the member is a real object. +// @ts-expect-error +sample.child?.missing; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterVariations.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterVariations.ts new file mode 100644 index 0000000000000..4f7f94d65d9b0 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterVariations.ts @@ -0,0 +1,76 @@ +// @strict: true +// @noEmit: true + +// The recursive getter written the several ways it comes up: as a class's static, nested inside +// another object literal, through an intermediate call, spelled out with an annotation, and twice +// over in one file. main reports TS7022/TS7023 on every one of these, and TS2502 on the annotated +// one. + +interface Schema { + readonly out: O; +} +type Shape = Record>; +declare function object(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { shape: S }; +declare function arr>(t: T): Schema; +declare const leaf: Schema; + +class Holder { + static readonly schema = object({ + name: leaf, + get self() { + return Holder.schema; + }, + }); +} +declare const fromClass: typeof Holder.schema.out; +const className: string = fromClass.name; + +const nested = object({ + name: leaf, + get inner() { + return object({ deeper: nested }); + }, +}); +declare const fromNested: typeof nested.out; +const nestedName: string = fromNested.inner.deeper.name; + +const viaCall = object({ + name: leaf, + get items() { + return arr(viaCall); + }, +}); +declare const fromCall: typeof viaCall.out; +const callName: string = fromCall.items[0].items[0].name; + +const annotated = object({ + name: leaf, + get self(): typeof annotated { + return annotated; + }, +}); +declare const fromAnnotated: typeof annotated.out; +const annotatedName: string = fromAnnotated.self.self.name; + +// Two independent recursive schemas must not contaminate each other. +const first = object({ a: leaf, get me() { return first; } }); +const second = object({ b: leaf, get me() { return second; } }); +declare const fromFirst: typeof first.out; +declare const fromSecond: typeof second.out; +const firstA: string = fromFirst.me.a; +const secondB: string = fromSecond.me.b; + +// Every assertion above still passes if the type degraded to `any`. These do not: each names a key +// the schema does not have, so the expected error is what shows the member is a real object type. +// @ts-expect-error +fromClass.self.missing; +// @ts-expect-error +fromNested.inner.deeper.missing; +// @ts-expect-error +fromCall.items[0].missing; +// @ts-expect-error +fromAnnotated.self.missing; +// @ts-expect-error +fromFirst.me.missing; +// @ts-expect-error +fromSecond.me.missing; From 7be19a420ade1ab6f80a45af9fb698fecb1da0b1 Mon Sep 17 00:00:00 2001 From: alt Date: Thu, 27 Aug 2026 09:08:14 -0700 Subject: [PATCH 3/9] Finish a property lookup that lands in the inherited-members window --- tsc/internal/checker/checker.go | 57 ++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index ce8771e604e8f..5898fbf22a11a 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -799,6 +799,7 @@ type Checker struct { unresolvableMembers int skippedMemberChecks []skippedMemberCheck recheckingSkippedMembers bool + inheritanceInFlight map[*Type]*pendingInheritance varianceStack []VarianceStackEntry apparentArgumentCount *int lastGetCombinedNodeFlagsNode *ast.Node @@ -19443,6 +19444,9 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr case t.flags&TypeFlagsObject != 0: resolved := c.resolveStructuredTypeMembers(t) symbol := resolved.members[name] + if symbol == nil && t.objectFlags&ObjectFlagsUnresolvedMembers != 0 { + symbol = c.getPendingInheritedProperty(t, name) + } if symbol != nil { if !includeTypeOnlyMembers && t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsValueModule != 0 && c.moduleSymbolLinks.Get(t.symbol).typeOnlyExportStarMap[name] != nil { // If this is the type of a module, `resolved.members.get(name)` might have effectively skipped over @@ -19684,11 +19688,15 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) thisArgument := core.LastOrNil(typeArguments) t.objectFlags |= ObjectFlagsUnresolvedMembers + // The table just published holds only what the type declares itself, so every inherited member + // reads as absent until the loop below adds it. Recording what is still to come lets a lookup in + // that window finish itself against the bases instead, so the window is never observable. + if c.inheritanceInFlight == nil { + c.inheritanceInFlight = make(map[*Type]*pendingInheritance) + } + c.inheritanceInFlight[t] = &pendingInheritance{mapper: mapper, thisArgument: thisArgument, baseTypes: baseTypes} for _, baseType := range baseTypes { - instantiatedBaseType := baseType - if thisArgument != nil { - instantiatedBaseType = c.getTypeWithThisArgument(c.instantiateType(baseType, mapper), thisArgument, false /*needsApparentType*/) - } + instantiatedBaseType := c.instantiateBaseType(baseType, mapper, thisArgument) members = c.addInheritedMembers(members, c.getPropertiesOfType(instantiatedBaseType)) callSignatures = core.Concatenate(callSignatures, c.getSignaturesOfType(instantiatedBaseType, SignatureKindCall)) constructSignatures = core.Concatenate(constructSignatures, c.getSignaturesOfType(instantiatedBaseType, SignatureKindConstruct)) @@ -19702,11 +19710,49 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters return findIndexInfo(indexInfos, info.keyType) == nil })) } + delete(c.inheritanceInFlight, t) t.objectFlags &^= ObjectFlagsUnresolvedMembers } c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) } +// What resolveObjectTypeMembers has left to inherit, for as long as ObjectFlagsUnresolvedMembers is set. +type pendingInheritance struct { + mapper *TypeMapper + thisArgument *Type + baseTypes []*Type +} + +func (c *Checker) instantiateBaseType(baseType *Type, mapper *TypeMapper, thisArgument *Type) *Type { + if thisArgument == nil { + return baseType + } + return c.getTypeWithThisArgument(c.instantiateType(baseType, mapper), thisArgument, false /*needsApparentType*/) +} + +// Answers a property lookup that missed on a table whose inherited members have not been added yet, by +// asking the base types the same question. The member it finds is the one the table is about to hold, +// so this is the completed lookup rather than a guess about it -- a name no base carries is still +// absent, and callers need no notion of a provisional answer. +func (c *Checker) getPendingInheritedProperty(t *Type, name string) *ast.Symbol { + pending := c.inheritanceInFlight[t] + if pending == nil { + return nil + } + // A base that reaches back through t must find the window closed, or the two would ask each other + // forever; with the entry withdrawn t answers from its published table, which is what the loop's + // own re-entry sees. + delete(c.inheritanceInFlight, t) + defer func() { c.inheritanceInFlight[t] = pending }() + for _, baseType := range pending.baseTypes { + instantiated := c.instantiateBaseType(baseType, pending.mapper, pending.thisArgument) + if prop := c.getPropertyOfTypeEx(instantiated, name, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/); prop != nil && !isStaticPrivateIdentifierProperty(prop) { + return prop + } + } + return nil +} + func findIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexInfo { for _, info := range indexInfos { if info.keyType == keyType { @@ -21956,6 +22002,9 @@ func (c *Checker) getPropertyOfObjectType(t *Type, name string) *ast.Symbol { if t.flags&TypeFlagsObject != 0 { resolved := c.resolveStructuredTypeMembers(t) symbol := resolved.members[name] + if symbol == nil && t.objectFlags&ObjectFlagsUnresolvedMembers != 0 { + symbol = c.getPendingInheritedProperty(t, name) + } if symbol != nil && c.symbolIsValue(symbol) { return symbol } From c9c7351f515b38cbc800335d88652fb47de14b8d Mon Sep 17 00:00:00 2001 From: alt Date: Thu, 27 Aug 2026 09:13:11 -0700 Subject: [PATCH 4/9] Keep the pending records on a stack rather than a map --- tsc/internal/checker/checker.go | 36 +++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 5898fbf22a11a..4807883aa693f 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -799,7 +799,7 @@ type Checker struct { unresolvableMembers int skippedMemberChecks []skippedMemberCheck recheckingSkippedMembers bool - inheritanceInFlight map[*Type]*pendingInheritance + inheritanceInFlight []pendingInheritance varianceStack []VarianceStackEntry apparentArgumentCount *int lastGetCombinedNodeFlagsNode *ast.Node @@ -19691,10 +19691,7 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters // The table just published holds only what the type declares itself, so every inherited member // reads as absent until the loop below adds it. Recording what is still to come lets a lookup in // that window finish itself against the bases instead, so the window is never observable. - if c.inheritanceInFlight == nil { - c.inheritanceInFlight = make(map[*Type]*pendingInheritance) - } - c.inheritanceInFlight[t] = &pendingInheritance{mapper: mapper, thisArgument: thisArgument, baseTypes: baseTypes} + c.inheritanceInFlight = append(c.inheritanceInFlight, pendingInheritance{t: t, mapper: mapper, thisArgument: thisArgument, baseTypes: baseTypes}) for _, baseType := range baseTypes { instantiatedBaseType := c.instantiateBaseType(baseType, mapper, thisArgument) members = c.addInheritedMembers(members, c.getPropertiesOfType(instantiatedBaseType)) @@ -19710,17 +19707,21 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters return findIndexInfo(indexInfos, info.keyType) == nil })) } - delete(c.inheritanceInFlight, t) + c.inheritanceInFlight = c.inheritanceInFlight[:len(c.inheritanceInFlight)-1] t.objectFlags &^= ObjectFlagsUnresolvedMembers } c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) } // What resolveObjectTypeMembers has left to inherit, for as long as ObjectFlagsUnresolvedMembers is set. +// These nest strictly, so they are a stack rather than a map -- one that reaches its depth early and +// allocates nothing thereafter. type pendingInheritance struct { + t *Type mapper *TypeMapper thisArgument *Type baseTypes []*Type + consulting bool } func (c *Checker) instantiateBaseType(baseType *Type, mapper *TypeMapper, thisArgument *Type) *Type { @@ -19735,17 +19736,22 @@ func (c *Checker) instantiateBaseType(baseType *Type, mapper *TypeMapper, thisAr // so this is the completed lookup rather than a guess about it -- a name no base carries is still // absent, and callers need no notion of a provisional answer. func (c *Checker) getPendingInheritedProperty(t *Type, name string) *ast.Symbol { - pending := c.inheritanceInFlight[t] - if pending == nil { - return nil + i := len(c.inheritanceInFlight) - 1 + for i >= 0 && c.inheritanceInFlight[i].t != t { + i-- } // A base that reaches back through t must find the window closed, or the two would ask each other - // forever; with the entry withdrawn t answers from its published table, which is what the loop's - // own re-entry sees. - delete(c.inheritanceInFlight, t) - defer func() { c.inheritanceInFlight[t] = pending }() - for _, baseType := range pending.baseTypes { - instantiated := c.instantiateBaseType(baseType, pending.mapper, pending.thisArgument) + // forever; marked as being consulted, t answers from its published table instead, which is what the + // loop's own re-entry sees. The index outlives a reallocation of the stack where a pointer into it + // would not, and stays valid because everything pushed above it is popped before this returns. + if i < 0 || c.inheritanceInFlight[i].consulting { + return nil + } + c.inheritanceInFlight[i].consulting = true + defer func() { c.inheritanceInFlight[i].consulting = false }() + mapper, thisArgument, baseTypes := c.inheritanceInFlight[i].mapper, c.inheritanceInFlight[i].thisArgument, c.inheritanceInFlight[i].baseTypes + for _, baseType := range baseTypes { + instantiated := c.instantiateBaseType(baseType, mapper, thisArgument) if prop := c.getPropertyOfTypeEx(instantiated, name, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/); prop != nil && !isStaticPrivateIdentifierProperty(prop) { return prop } From bdde92ba8c43ea34e8b2aaa60694852281bf64b5 Mon Sep 17 00:00:00 2001 From: Colin McDonnell <3084745+colinhacks@users.noreply.github.com> Date: Fri, 4 Sep 2026 00:33:04 -0700 Subject: [PATCH 5/9] Keep a provisional comparison's circularity out of the program A provisional comparison explores further than an ordinary check does, so it can walk into a circular base constraint that main never reaches -- main collapses the getter first and stops. Reporting that circularity, or caching it as the type parameter's resolved constraint, adds a TS2313 to a program main accepts. The region reports nothing and decides nothing by construction, so the circularity is the question's own: it neither reports nor sticks, and the next ask outside any region is free to reach and report a real one. The new case pins that this variant, which neither main nor this change resolves, reports the same thing on both. --- tsc/internal/checker/checker.go | 10 +- ...bjectLiteralGetterReverseMapped.errors.txt | 61 +++++++ ...ghObjectLiteralGetterReverseMapped.symbols | 149 ++++++++++++++++++ ...oughObjectLiteralGetterReverseMapped.types | 98 ++++++++++++ ...ThroughObjectLiteralGetterReverseMapped.ts | 54 +++++++ 5 files changed, 370 insertions(+), 2 deletions(-) create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.types create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.ts diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 4807883aa693f..efb44a883de2c 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -28103,8 +28103,13 @@ func (c *Checker) getResolvedBaseConstraint(t *Type, stack []RecursionId) *Type if len(stack) < 10 || len(stack) < 50 && !slices.Contains(stack, identity) { constraint = c.computeBaseConstraint(c.getSimplifiedType(t /*writing*/, false), append(stack, identity)) } + circular := false if !c.popTypeResolution() { - if t.flags&TypeFlagsTypeParameter != 0 { + // A provisional comparison reports nothing and decides nothing, so a circularity it runs into is + // the question's own and neither reports nor sticks. It explores further than an ordinary check + // does -- that is the point of it -- and finding a cycle out there says nothing about the program. + // Declining the cache leaves the next ask, outside any region, to reach and report a real one. + if t.flags&TypeFlagsTypeParameter != 0 && c.provisionalDepth == 0 { errorNode := c.getConstraintDeclaration(t) if errorNode != nil { diagnostic := c.error(errorNode, diagnostics.Type_parameter_0_has_a_circular_constraint, c.TypeToString(t)) @@ -28114,11 +28119,12 @@ func (c *Checker) getResolvedBaseConstraint(t *Type, stack []RecursionId) *Type } } constraint = c.circularConstraintType + circular = true } if constraint == nil { constraint = c.noConstraintType } - if constrained.resolvedBaseConstraint == nil { + if constrained.resolvedBaseConstraint == nil && !(circular && c.provisionalDepth != 0) { constrained.resolvedBaseConstraint = constraint } return constraint diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.errors.txt b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.errors.txt new file mode 100644 index 0000000000000..8bc4502a50af7 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.errors.txt @@ -0,0 +1,61 @@ +recursiveTypeThroughObjectLiteralGetterReverseMapped.ts(43,7): error TS7022: 'category' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. +recursiveTypeThroughObjectLiteralGetterReverseMapped.ts(45,9): error TS7023: 'parent' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. + + +==== recursiveTypeThroughObjectLiteralGetterReverseMapped.ts (2 errors) ==== + // The same schema shape, but the parameter is written as a mapped type over the inferred one, so + // inference runs through a reverse mapped type. Neither this change nor main resolves this variant -- + // both report TS7022/TS7023 on the declaration -- and the point of the case is that they report the + // SAME thing. + // + // A provisional comparison explores further than an ordinary check does, so it can walk into a + // circularity that main never reaches because it collapses the getter first. Reporting that, or + // caching it as the type parameter's constraint, adds a diagnostic to a program main accepts. The + // circularity belongs to the question, so inside a region it neither reports nor sticks, and the next + // ask outside one is free to reach a real one. + + interface Internals { + optional: "true" | "false"; + out: O; + } + + interface StringSchema extends Internals { + optional: "false"; + } + + type Shape = Record; + type Prettify = { [K in keyof T]: T[K] } & {}; + type ObjectOut = Prettify< + { + [K in keyof S as S[K] extends { optional: "true" } ? K : never]?: S[K]["out"]; + } & { + [K in keyof S as S[K] extends { optional: "true" } ? never : K]: S[K]["out"]; + } + >; + + interface ObjectSchema extends Internals> { + optional: "false"; + } + + interface OptionalSchema> extends Internals { + optional: "true"; + } + + declare function object(shape: { [K in keyof S]: S[K] }): ObjectSchema; + declare function text(): StringSchema; + declare function optional>(schema: T): OptionalSchema; + + const category = object({ + ~~~~~~~~ +!!! error TS7022: 'category' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + name: text(), + get parent() { + ~~~~~~ +!!! error TS7023: 'parent' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. + return optional(category); + }, + }); + + declare const sample: (typeof category)["out"]; + const topName: string = sample.name; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.symbols new file mode 100644 index 0000000000000..3e59cda5533df --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.symbols @@ -0,0 +1,149 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterReverseMapped.ts === +// The same schema shape, but the parameter is written as a mapped type over the inferred one, so +// inference runs through a reverse mapped type. Neither this change nor main resolves this variant -- +// both report TS7022/TS7023 on the declaration -- and the point of the case is that they report the +// SAME thing. +// +// A provisional comparison explores further than an ordinary check does, so it can walk into a +// circularity that main never reaches because it collapses the getter first. Reporting that, or +// caching it as the type parameter's constraint, adds a diagnostic to a program main accepts. The +// circularity belongs to the question, so inside a region it neither reports nor sticks, and the next +// ask outside one is free to reach a real one. + +interface Internals { +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 0, 0)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 11, 20)) + + optional: "true" | "false"; +>optional : Symbol(Internals.optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 11, 24)) + + out: O; +>out : Symbol(Internals.out, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 12, 31)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 11, 20)) +} + +interface StringSchema extends Internals { +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 14, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 0, 0)) + + optional: "false"; +>optional : Symbol(StringSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 16, 50)) +} + +type Shape = Record; +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 18, 1)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +type Prettify = { [K in keyof T]: T[K] } & {}; +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 20, 33)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 14)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 22)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 14)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 14)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 22)) + +type ObjectOut = Prettify< +>ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 49)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 18, 1)) +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 20, 33)) + { + [K in keyof S as S[K] extends { optional: "true" } ? K : never]?: S[K]["out"]; +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 24, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 24, 9)) +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 24, 39)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 24, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 24, 9)) + + } & { + [K in keyof S as S[K] extends { optional: "true" } ? never : K]: S[K]["out"]; +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 26, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 26, 9)) +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 26, 39)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 26, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 26, 9)) + } +>; + +interface ObjectSchema extends Internals> { +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 28, 2)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 30, 23)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 18, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 0, 0)) +>ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 49)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 30, 23)) + + optional: "false"; +>optional : Symbol(ObjectSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 30, 73)) +} + +interface OptionalSchema> extends Internals { +>OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 32, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 34, 25)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 0, 0)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 34, 25)) + + optional: "true"; +>optional : Symbol(OptionalSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 34, 92)) +} + +declare function object(shape: { [K in keyof S]: S[K] }): ObjectSchema; +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 36, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 18, 1)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 41)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 51)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 24)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 24)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 51)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 28, 2)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 24)) + +declare function text(): StringSchema; +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 91)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 14, 1)) + +declare function optional>(schema: T): OptionalSchema; +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 39, 38)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 40, 26)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 0, 0)) +>schema : Symbol(schema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 40, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 40, 26)) +>OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 32, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 40, 26)) + +const category = object({ +>category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 42, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 36, 1)) + + name: text(), +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 42, 25)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 91)) + + get parent() { +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 43, 17)) + + return optional(category); +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 39, 38)) +>category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 42, 5)) + + }, +}); + +declare const sample: (typeof category)["out"]; +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 49, 13)) +>category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 42, 5)) + +const topName: string = sample.name; +>topName : Symbol(topName, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 50, 5)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 49, 13)) + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.types new file mode 100644 index 0000000000000..6180bf9b3a638 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.types @@ -0,0 +1,98 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterReverseMapped.ts === +// The same schema shape, but the parameter is written as a mapped type over the inferred one, so +// inference runs through a reverse mapped type. Neither this change nor main resolves this variant -- +// both report TS7022/TS7023 on the declaration -- and the point of the case is that they report the +// SAME thing. +// +// A provisional comparison explores further than an ordinary check does, so it can walk into a +// circularity that main never reaches because it collapses the getter first. Reporting that, or +// caching it as the type parameter's constraint, adds a diagnostic to a program main accepts. The +// circularity belongs to the question, so inside a region it neither reports nor sticks, and the next +// ask outside one is free to reach a real one. + +interface Internals { + optional: "true" | "false"; +>optional : "false" | "true" + + out: O; +>out : O +} + +interface StringSchema extends Internals { + optional: "false"; +>optional : "false" +} + +type Shape = Record; +>Shape : Shape + +type Prettify = { [K in keyof T]: T[K] } & {}; +>Prettify : { [K in keyof T]: T[K]; } + +type ObjectOut = Prettify< +>ObjectOut : { [K in keyof S as S[K] extends { optional: "true"; } ? K : never]?: S[K]["out"] | undefined; } & { [K in keyof S as S[K] extends { optional: "true"; } ? never : K]: S[K]["out"]; } extends infer T ? { [K in keyof T]: T[K]; } : never + { + [K in keyof S as S[K] extends { optional: "true" } ? K : never]?: S[K]["out"]; +>optional : "true" + + } & { + [K in keyof S as S[K] extends { optional: "true" } ? never : K]: S[K]["out"]; +>optional : "true" + } +>; + +interface ObjectSchema extends Internals> { + optional: "false"; +>optional : "false" +} + +interface OptionalSchema> extends Internals { + optional: "true"; +>optional : "true" +} + +declare function object(shape: { [K in keyof S]: S[K] }): ObjectSchema; +>object : (shape: { [K in keyof S]: S[K]; }) => ObjectSchema +>shape : { [K in keyof S]: S[K]; } + +declare function text(): StringSchema; +>text : () => StringSchema + +declare function optional>(schema: T): OptionalSchema; +>optional : >(schema: T) => OptionalSchema +>schema : T + +const category = object({ +>category : any +>object({ name: text(), get parent() { return optional(category); },}) : ObjectSchema<{ name: StringSchema; readonly parent: any; }> +>object : (shape: { [K in keyof S]: S[K]; }) => ObjectSchema +>{ name: text(), get parent() { return optional(category); },} : { name: StringSchema; readonly parent: any; } + + name: text(), +>name : StringSchema +>text() : StringSchema +>text : () => StringSchema + + get parent() { +>parent : any + + return optional(category); +>optional(category) : OptionalSchema +>optional : >(schema: T) => OptionalSchema +>category : any + + }, +}); + +declare const sample: (typeof category)["out"]; +>sample : any +>category : any + +const topName: string = sample.name; +>topName : string +>sample.name : any +>sample : any +>name : any + diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.ts new file mode 100644 index 0000000000000..c533a65fce85d --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.ts @@ -0,0 +1,54 @@ +// @strict: true +// @noEmit: true + +// The same schema shape, but the parameter is written as a mapped type over the inferred one, so +// inference runs through a reverse mapped type. Neither this change nor main resolves this variant -- +// both report TS7022/TS7023 on the declaration -- and the point of the case is that they report the +// SAME thing. +// +// A provisional comparison explores further than an ordinary check does, so it can walk into a +// circularity that main never reaches because it collapses the getter first. Reporting that, or +// caching it as the type parameter's constraint, adds a diagnostic to a program main accepts. The +// circularity belongs to the question, so inside a region it neither reports nor sticks, and the next +// ask outside one is free to reach a real one. + +interface Internals { + optional: "true" | "false"; + out: O; +} + +interface StringSchema extends Internals { + optional: "false"; +} + +type Shape = Record; +type Prettify = { [K in keyof T]: T[K] } & {}; +type ObjectOut = Prettify< + { + [K in keyof S as S[K] extends { optional: "true" } ? K : never]?: S[K]["out"]; + } & { + [K in keyof S as S[K] extends { optional: "true" } ? never : K]: S[K]["out"]; + } +>; + +interface ObjectSchema extends Internals> { + optional: "false"; +} + +interface OptionalSchema> extends Internals { + optional: "true"; +} + +declare function object(shape: { [K in keyof S]: S[K] }): ObjectSchema; +declare function text(): StringSchema; +declare function optional>(schema: T): OptionalSchema; + +const category = object({ + name: text(), + get parent() { + return optional(category); + }, +}); + +declare const sample: (typeof category)["out"]; +const topName: string = sample.name; From 2fddb004ac4504910f836640ae495153e30b709c Mon Sep 17 00:00:00 2001 From: Colin McDonnell <3084745+colinhacks@users.noreply.github.com> Date: Fri, 4 Sep 2026 01:00:42 -0700 Subject: [PATCH 6/9] Answer a miss in the inherited-members window by who is asking resolveObjectTypeMembers publishes the self-declared member table before it walks the base types, so every inherited member reads as absent until the loop adds it. A miss in that window has two correct answers, and the previous commit merged them into one. Inside a provisional comparison the asker is a speculative check whose whole job is to find out whether a member can answer yet, so completing the lookup there forces the exact type the question is about. Outside one there is no question in flight and a miss is just a lookup that arrived early, so finishing it against the bases still to be inherited is what keeps the window unobservable. Gate the completed lookup on provisionalDepth == 0 and restore the narrower suppression for the inside-a-region case. Measured on a schema-library corpus, counting the getter-collapsed-to-any diagnostic this series exists to remove: one answer everywhere gives 5, restoring the suppression without the gate gives 3, dropping the suppression and gating gives 12, and both together give 0 -- which is what the inference fix gives on its own, so #62180 now closes at no cost to #62181. Add recursiveTypeThroughObjectLiteralGetterMutual.ts, which is the only case in the suite that reaches this. It needs two schemas naming each other through getters and two separate key remappings over the same shape, one per variance side. With a single remapping every variant above passes, which is why thirteen existing cases and a 498-project corpus all missed the regression. --- tsc/internal/checker/checker.go | 62 ++-- ...ectLiteralGetterInheritedMember.errors.txt | 77 ----- ...ObjectLiteralGetterInheritedMember.symbols | 2 + ...ghObjectLiteralGetterInheritedMember.types | 18 +- ...peThroughObjectLiteralGetterMutual.symbols | 275 ++++++++++++++++++ ...TypeThroughObjectLiteralGetterMutual.types | 181 ++++++++++++ ...iveTypeThroughObjectLiteralGetterMutual.ts | 89 ++++++ 7 files changed, 587 insertions(+), 117 deletions(-) delete mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.types create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterMutual.ts diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index efb44a883de2c..9b2b42368221d 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -19116,6 +19116,35 @@ func (c *Checker) getCombinedModifierFlagsCached(node *ast.Node) ast.ModifierFla return c.lastGetCombinedModifierFlagsResult } +// A constraint comparison postponed because the member could not be worked out yet. +type skippedMemberCheck struct { + property *ast.Symbol + target *Type + relation *Relation + errorNode *ast.Node +} + +// Raised when a resolution started by a provisional question re-enters a declaration that was already +// being resolved when that question was asked. Nothing computed from the circular value is allowed to +// complete, so no diagnostic is reported and no cache records it. +type circularQuestion struct{} + +// The checker's push/pop state, so an abandoned attempt can be unwound without leaving a push behind. +type checkerStacks struct { + typeResolutions, varianceStack, activeMappers, activeTypeMappersCaches int + antecedentTypes, awaitedTypeStack, contextualBindingPatterns int + contextualInfos, flowLoopStack, inferenceContextInfos, sharedFlows int + reverseMappedSourceStack, reverseMappedTargetStack int + renamedBindingElementsInTypes, resolutionStart int + instantiationDepth, conditionalConstraintDepth uint32 + currentNode *ast.Node + varianceTypeParameter *Type + flowTypeCache map[*ast.Node]*Type + reliabilityFlags RelationComparisonResult + reverseExpandingFlags ExpandingFlags + flowAnalysisDisabled, withinUnreachableCode bool +} + // Whether any base of t could still contribute a member named name. resolveObjectTypeMembers publishes // the self-declared table before it walks the bases, so a miss inside that window is "not known yet" // rather than "absent" -- but only for a name a base actually declares. Reading declaration tables @@ -19151,35 +19180,6 @@ func (c *Checker) mayInheritProperty(t *Type, name string, seen []*Type) bool { return false } -// A constraint comparison postponed because the member could not be worked out yet. -type skippedMemberCheck struct { - property *ast.Symbol - target *Type - relation *Relation - errorNode *ast.Node -} - -// Raised when a resolution started by a provisional question re-enters a declaration that was already -// being resolved when that question was asked. Nothing computed from the circular value is allowed to -// complete, so no diagnostic is reported and no cache records it. -type circularQuestion struct{} - -// The checker's push/pop state, so an abandoned attempt can be unwound without leaving a push behind. -type checkerStacks struct { - typeResolutions, varianceStack, activeMappers, activeTypeMappersCaches int - antecedentTypes, awaitedTypeStack, contextualBindingPatterns int - contextualInfos, flowLoopStack, inferenceContextInfos, sharedFlows int - reverseMappedSourceStack, reverseMappedTargetStack int - renamedBindingElementsInTypes, resolutionStart int - instantiationDepth, conditionalConstraintDepth uint32 - currentNode *ast.Node - varianceTypeParameter *Type - flowTypeCache map[*ast.Node]*Type - reliabilityFlags RelationComparisonResult - reverseExpandingFlags ExpandingFlags - flowAnalysisDisabled, withinUnreachableCode bool -} - func (c *Checker) saveStacks() checkerStacks { return checkerStacks{ typeResolutions: len(c.typeResolutions), varianceStack: len(c.varianceStack), @@ -19444,7 +19444,7 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr case t.flags&TypeFlagsObject != 0: resolved := c.resolveStructuredTypeMembers(t) symbol := resolved.members[name] - if symbol == nil && t.objectFlags&ObjectFlagsUnresolvedMembers != 0 { + if symbol == nil && t.objectFlags&ObjectFlagsUnresolvedMembers != 0 && c.provisionalDepth == 0 { symbol = c.getPendingInheritedProperty(t, name) } if symbol != nil { @@ -22008,7 +22008,7 @@ func (c *Checker) getPropertyOfObjectType(t *Type, name string) *ast.Symbol { if t.flags&TypeFlagsObject != 0 { resolved := c.resolveStructuredTypeMembers(t) symbol := resolved.members[name] - if symbol == nil && t.objectFlags&ObjectFlagsUnresolvedMembers != 0 { + if symbol == nil && t.objectFlags&ObjectFlagsUnresolvedMembers != 0 && c.provisionalDepth == 0 { symbol = c.getPendingInheritedProperty(t, name) } if symbol != nil && c.symbolIsValue(symbol) { diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.errors.txt b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.errors.txt deleted file mode 100644 index c466d15278fa3..0000000000000 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.errors.txt +++ /dev/null @@ -1,77 +0,0 @@ -recursiveTypeThroughObjectLiteralGetterInheritedMember.ts(61,43): error TS2339: Property 'name' does not exist on type '{}'. - - -==== recursiveTypeThroughObjectLiteralGetterInheritedMember.ts (1 errors) ==== - // The shape from #62180, where the recursive member is reached through a mapped type that remaps its - // keys by reading each member's own discriminant. Resolving the object schema's members instantiates - // its base with a type that names the schema back, so the schema's table is read while it is still - // being assembled and its inherited `out` is not in it yet. - // - // This case is NOT fixed, and the baseline records how far it gets. An earlier draft answered every - // miss in that window with `any`, which cleared the error here -- but that answer is not confined to - // this shape. A self-referential interface reaches the same window with no getter and no inference - // anywhere in the program, and an unpatched compiler answers it from the index signature; making it - // `any` silently accepted an indexed access that every other compiler rejects. Nothing available at - // the miss distinguishes the two, so the answer was withdrawn rather than guessed. - // - // What is left is still an improvement on main, which cannot type the declaration at all: the - // getter resolves, the non-recursive members are real types, and only the remapped member is - // unresolved. - - interface Internals { - optional: "true" | "false"; - out: O; - } - - interface StringSchema extends Internals { - optional: "false"; - } - - type Shape = Record; - type Prettify = { [K in keyof T]: T[K] } & {}; - type ObjectOut = Prettify< - { - [K in keyof S as S[K] extends { optional: "true" } ? K : never]?: S[K]["out"]; - } & { - [K in keyof S as S[K] extends { optional: "true" } ? never : K]: S[K]["out"]; - } - >; - - interface ObjectSchema extends Internals> { - optional: "false"; - } - - interface OptionalSchema> extends Internals { - optional: "true"; - } - - declare function object(shape: S): ObjectSchema; - declare function text(): StringSchema; - declare function optional>(schema: T): OptionalSchema; - - const category = object({ - name: text(), - get parent() { - return optional(category); - }, - }); - - declare const sample: (typeof category)["out"]; - // The key that does not recurse resolves fully, and the remapping put the recursive one in the - // optional bucket, so it is `parent?:` rather than `parent:`. Both of those are new: main gives the - // whole declaration an implicit `any` and reports it. - const topName: string = sample.name; - // The recursive one does not resolve. Pinned so that closing this shows up here. - const parentName: string = sample.parent!.name; - ~~~~ -!!! error TS2339: Property 'name' does not exist on type '{}'. - - // A property that is genuinely absent must still be reported, whoever is asking. - interface Base { readonly own: number; } - interface Derived extends Base { readonly extra: string; } - declare const derived: Derived; - // @ts-expect-error - derived.missing; - const ownValue: number = derived.own; - const extraValue: string = derived.extra; - \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols index 7fee19cde7e3c..527542b1c3c08 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols @@ -157,9 +157,11 @@ const topName: string = sample.name; // The recursive one does not resolve. Pinned so that closing this shows up here. const parentName: string = sample.parent!.name; >parentName : Symbol(parentName, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 60, 5)) +>sample.parent!.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) >sample.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) >sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) >parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) // A property that is genuinely absent must still be reported, whoever is asking. interface Base { readonly own: number; } diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types index 57635cce272dc..81bc7984cb74e 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types @@ -92,7 +92,7 @@ const category = object({ }); declare const sample: (typeof category)["out"]; ->sample : { name: string; readonly parent?: unknown; } +>sample : { name: string; readonly parent?: { name: string; readonly parent?: any | undefined; } | undefined; } >category : ObjectSchema<{ name: StringSchema; readonly parent: OptionalSchema>; }> // The key that does not recurse resolves fully, and the remapping put the recursive one in the @@ -101,18 +101,18 @@ declare const sample: (typeof category)["out"]; const topName: string = sample.name; >topName : string >sample.name : string ->sample : { name: string; readonly parent?: unknown; } +>sample : { name: string; readonly parent?: { name: string; readonly parent?: any | undefined; } | undefined; } >name : string // The recursive one does not resolve. Pinned so that closing this shows up here. const parentName: string = sample.parent!.name; >parentName : string ->sample.parent!.name : any ->sample.parent! : {} ->sample.parent : unknown ->sample : { name: string; readonly parent?: unknown; } ->parent : unknown ->name : any +>sample.parent!.name : string +>sample.parent! : { name: string; readonly parent?: any | undefined; } +>sample.parent : { name: string; readonly parent?: any | undefined; } | undefined +>sample : { name: string; readonly parent?: { name: string; readonly parent?: any | undefined; } | undefined; } +>parent : { name: string; readonly parent?: any | undefined; } | undefined +>name : string // A property that is genuinely absent must still be reported, whoever is asking. interface Base { readonly own: number; } @@ -126,7 +126,7 @@ declare const derived: Derived; // @ts-expect-error derived.missing; ->derived.missing : any +>derived.missing : error >derived : Derived >missing : any diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.symbols new file mode 100644 index 0000000000000..73119b911f195 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.symbols @@ -0,0 +1,275 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterMutual.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterMutual.ts === +// Two schemas that name each other through getters, with the object's members reached through two +// separate key remappings -- one for the output side and one for the input side, each keyed on a +// different member of the schema's own internals. +// +// Both remappings read the member table of a type whose bases are still being inherited, and they +// read it at different points in that assembly. That is what makes this shape the one that separates +// the two answers a miss in that window can have. A speculative comparison has to decline, because +// the member it would force is the one its own question is about. An ordinary lookup has to finish +// itself against the base types still to be inherited, which is what keeps the window unobservable. +// Giving a speculative miss the second answer forces `posts` while its own return type is still +// being inferred, and the getter falls back to an implicit `any` -- so the two cannot be merged into +// one answer, and a single remapping does not reach the case. + +interface Internals { +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 0, 0)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 13, 20)) +>I : Symbol(I, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 13, 36)) + + output: O; +>output : Symbol(Internals.output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 13, 55)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 13, 20)) + + input: I; +>input : Symbol(Internals.input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 14, 14)) +>I : Symbol(I, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 13, 36)) + + optin?: "optional" | undefined; +>optin : Symbol(Internals.optin, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 15, 13)) + + optout?: "optional" | undefined; +>optout : Symbol(Internals.optout, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 16, 35)) +} + +interface Schema { +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 20, 17)) +>I : Symbol(I, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 20, 33)) + + _zod: Internals; +>_zod : Symbol(Schema._zod, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 20, 52)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 0, 0)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 20, 17)) +>I : Symbol(I, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 20, 33)) +} + +type output = T["_zod"]["output"]; +>output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 12)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 12)) + +type input = T["_zod"]["input"]; +>input : Symbol(input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 25, 11)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 25, 11)) + +interface StringSchema extends Schema {} +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 25, 50)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) + +declare function text(): StringSchema; +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 27, 56)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 25, 50)) + +interface ArraySchema extends Schema[], input[]> {} +>ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 28, 38)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 22)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) +>output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 22)) +>input : Symbol(input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 22)) + +declare function array(element: T): ArraySchema; +>array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 91)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 23)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) +>element : Symbol(element, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 41)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 23)) +>ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 28, 38)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 23)) + +type OptionalOutSchema = { _zod: { optout: "optional" } }; +>OptionalOutSchema : Symbol(OptionalOutSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 69)) +>_zod : Symbol(_zod, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 26)) +>optout : Symbol(optout, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 34)) + +type OptionalInSchema = { _zod: { optin: "optional" } }; +>OptionalInSchema : Symbol(OptionalInSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 58)) +>_zod : Symbol(_zod, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 25)) +>optin : Symbol(optin, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 33)) + +type Shape = Readonly<{ [k: string]: Schema }>; +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 56)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) + +type Prettify = { [K in keyof T]: T[K] } & {}; +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 47)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 14)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 22)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 14)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 14)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 22)) + +type ObjectOut = Prettify< +>ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 49)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 56)) +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 47)) + { + [k in keyof T as T[k] extends OptionalOutSchema ? never : k]: output; +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 40, 9)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 40, 9)) +>OptionalOutSchema : Symbol(OptionalOutSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 69)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 40, 9)) +>output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 40, 9)) + + } & { + [k in keyof T as T[k] extends OptionalOutSchema ? k : never]?: output; +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 42, 9)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 42, 9)) +>OptionalOutSchema : Symbol(OptionalOutSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 69)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 42, 9)) +>output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 42, 9)) + } +>; + +type ObjectIn = Prettify< +>ObjectIn : Symbol(ObjectIn, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 44, 2)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 56)) +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 47)) + { + [k in keyof T as T[k] extends OptionalInSchema ? never : k]: input; +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 48, 9)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 48, 9)) +>OptionalInSchema : Symbol(OptionalInSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 58)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 48, 9)) +>input : Symbol(input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 48, 9)) + + } & { + [k in keyof T as T[k] extends OptionalInSchema ? k : never]?: input; +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 50, 9)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 50, 9)) +>OptionalInSchema : Symbol(OptionalInSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 58)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 50, 9)) +>input : Symbol(input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 50, 9)) + } +>; + +interface ObjectSchema extends Schema, ObjectIn> { +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 52, 2)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 23)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 56)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 56)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) +>ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 49)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 23)) +>ObjectIn : Symbol(ObjectIn, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 44, 2)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 23)) + + shape: S; +>shape : Symbol(ObjectSchema.shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 91)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 23)) +} +declare function object(shape: T): ObjectSchema; +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 57, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 56)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 57, 41)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 57, 24)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 52, 2)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 57, 24)) + +const user = object({ +>user : Symbol(user, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 1)) + + email: text(), +>email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 21)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 27, 56)) + + get posts() { +>posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 60, 18)) + + return array(post); +>array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 91)) +>post : Symbol(post, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 66, 5)) + + }, +}); + +const post = object({ +>post : Symbol(post, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 66, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 1)) + + title: text(), +>title : Symbol(title, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 66, 21)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 27, 56)) + + get author() { +>author : Symbol(author, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 67, 18)) + + return user; +>user : Symbol(user, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 5)) + + }, +}); + +type UserT = output; +>UserT : Symbol(UserT, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 71, 3)) +>output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 1)) +>user : Symbol(user, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 5)) + +declare const sampleUser: UserT; +>sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 74, 13)) +>UserT : Symbol(UserT, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 71, 3)) + +// Every step of the cycle resolves, including the one that walks back to where it started. On main +// both declarations get an implicit `any` instead. +const email: string = sampleUser.email; +>email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 77, 5)) +>sampleUser.email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 21)) +>sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 74, 13)) +>email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 21)) + +const postTitle: string = sampleUser.posts[0].title; +>postTitle : Symbol(postTitle, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 78, 5)) +>sampleUser.posts[0].title : Symbol(title, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 66, 21)) +>sampleUser.posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 60, 18)) +>sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 74, 13)) +>posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 60, 18)) +>title : Symbol(title, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 66, 21)) + +const authorEmail: string = sampleUser.posts[0].author.email; +>authorEmail : Symbol(authorEmail, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 79, 5)) +>sampleUser.posts[0].author.email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 21)) +>sampleUser.posts[0].author : Symbol(author, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 67, 18)) +>sampleUser.posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 60, 18)) +>sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 74, 13)) +>posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 60, 18)) +>author : Symbol(author, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 67, 18)) +>email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 21)) + +// The printer elides a recursive reference as `any`, so a collapsed result prints the same as a +// resolved one. Reading a key neither schema declares is what tells them apart: it is an error here +// and permitted on `any`, so a collapse turns this directive into an unused one. +// @ts-expect-error +sampleUser.missing; +>sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 74, 13)) + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.types new file mode 100644 index 0000000000000..2925d74f40c01 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.types @@ -0,0 +1,181 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterMutual.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterMutual.ts === +// Two schemas that name each other through getters, with the object's members reached through two +// separate key remappings -- one for the output side and one for the input side, each keyed on a +// different member of the schema's own internals. +// +// Both remappings read the member table of a type whose bases are still being inherited, and they +// read it at different points in that assembly. That is what makes this shape the one that separates +// the two answers a miss in that window can have. A speculative comparison has to decline, because +// the member it would force is the one its own question is about. An ordinary lookup has to finish +// itself against the base types still to be inherited, which is what keeps the window unobservable. +// Giving a speculative miss the second answer forces `posts` while its own return type is still +// being inferred, and the getter falls back to an implicit `any` -- so the two cannot be merged into +// one answer, and a single remapping does not reach the case. + +interface Internals { + output: O; +>output : O + + input: I; +>input : I + + optin?: "optional" | undefined; +>optin : "optional" | undefined + + optout?: "optional" | undefined; +>optout : "optional" | undefined +} + +interface Schema { + _zod: Internals; +>_zod : Internals +} + +type output = T["_zod"]["output"]; +>output : output + +type input = T["_zod"]["input"]; +>input : input + +interface StringSchema extends Schema {} +declare function text(): StringSchema; +>text : () => StringSchema + +interface ArraySchema extends Schema[], input[]> {} +declare function array(element: T): ArraySchema; +>array : (element: T) => ArraySchema +>element : T + +type OptionalOutSchema = { _zod: { optout: "optional" } }; +>OptionalOutSchema : OptionalOutSchema +>_zod : { optout: "optional"; } +>optout : "optional" + +type OptionalInSchema = { _zod: { optin: "optional" } }; +>OptionalInSchema : OptionalInSchema +>_zod : { optin: "optional"; } +>optin : "optional" + +type Shape = Readonly<{ [k: string]: Schema }>; +>Shape : Readonly<{ [k: string]: Schema; }> +>k : string + +type Prettify = { [K in keyof T]: T[K] } & {}; +>Prettify : { [K in keyof T]: T[K]; } + +type ObjectOut = Prettify< +>ObjectOut : { [k in keyof T as T[k] extends OptionalOutSchema ? never : k]: output; } & { [k in keyof T as T[k] extends OptionalOutSchema ? k : never]?: output | undefined; } extends infer T_1 ? { [K in keyof T_1]: T_1[K]; } : never + { + [k in keyof T as T[k] extends OptionalOutSchema ? never : k]: output; + } & { + [k in keyof T as T[k] extends OptionalOutSchema ? k : never]?: output; + } +>; + +type ObjectIn = Prettify< +>ObjectIn : { [k in keyof T as T[k] extends OptionalInSchema ? never : k]: input; } & { [k in keyof T as T[k] extends OptionalInSchema ? k : never]?: input | undefined; } extends infer T_1 ? { [K in keyof T_1]: T_1[K]; } : never + { + [k in keyof T as T[k] extends OptionalInSchema ? never : k]: input; + } & { + [k in keyof T as T[k] extends OptionalInSchema ? k : never]?: input; + } +>; + +interface ObjectSchema extends Schema, ObjectIn> { + shape: S; +>shape : S +} +declare function object(shape: T): ObjectSchema; +>object : (shape: T) => ObjectSchema +>shape : T + +const user = object({ +>user : ObjectSchema<{ email: StringSchema; readonly posts: ArraySchema; }>>; }> +>object({ email: text(), get posts() { return array(post); },}) : ObjectSchema<{ email: StringSchema; readonly posts: ArraySchema; }>>; }> +>object : (shape: T) => ObjectSchema +>{ email: text(), get posts() { return array(post); },} : { email: StringSchema; readonly posts: ArraySchema>; }>; }>>; } + + email: text(), +>email : StringSchema +>text() : StringSchema +>text : () => StringSchema + + get posts() { +>posts : ArraySchema>; }>; }>> + + return array(post); +>array(post) : ArraySchema>; }>; }>> +>array : (element: T) => ArraySchema +>post : ObjectSchema<{ title: StringSchema; readonly author: ObjectSchema<{ email: StringSchema; readonly posts: ArraySchema>; }>; }> + + }, +}); + +const post = object({ +>post : ObjectSchema<{ title: StringSchema; readonly author: ObjectSchema<{ email: StringSchema; readonly posts: ArraySchema>; }>; }> +>object({ title: text(), get author() { return user; },}) : ObjectSchema<{ title: StringSchema; readonly author: ObjectSchema<{ email: StringSchema; readonly posts: ArraySchema>; }>; }> +>object : (shape: T) => ObjectSchema +>{ title: text(), get author() { return user; },} : { title: StringSchema; readonly author: ObjectSchema<{ email: StringSchema; readonly posts: ArraySchema; }>>; }>; } + + title: text(), +>title : StringSchema +>text() : StringSchema +>text : () => StringSchema + + get author() { +>author : ObjectSchema<{ email: StringSchema; readonly posts: ArraySchema; }>>; }> + + return user; +>user : ObjectSchema<{ email: StringSchema; readonly posts: ArraySchema; }>>; }> + + }, +}); + +type UserT = output; +>UserT : { email: string; readonly posts: { title: string; readonly author: any; }[]; } +>user : ObjectSchema<{ email: StringSchema; readonly posts: ArraySchema; }>>; }> + +declare const sampleUser: UserT; +>sampleUser : { email: string; readonly posts: { title: string; readonly author: any; }[]; } + +// Every step of the cycle resolves, including the one that walks back to where it started. On main +// both declarations get an implicit `any` instead. +const email: string = sampleUser.email; +>email : string +>sampleUser.email : string +>sampleUser : { email: string; readonly posts: { title: string; readonly author: any; }[]; } +>email : string + +const postTitle: string = sampleUser.posts[0].title; +>postTitle : string +>sampleUser.posts[0].title : string +>sampleUser.posts[0] : { title: string; readonly author: { email: string; readonly posts: any[]; }; } +>sampleUser.posts : { title: string; readonly author: { email: string; readonly posts: any[]; }; }[] +>sampleUser : { email: string; readonly posts: { title: string; readonly author: any; }[]; } +>posts : { title: string; readonly author: { email: string; readonly posts: any[]; }; }[] +>0 : 0 +>title : string + +const authorEmail: string = sampleUser.posts[0].author.email; +>authorEmail : string +>sampleUser.posts[0].author.email : string +>sampleUser.posts[0].author : { email: string; readonly posts: { title: string; readonly author: any; }[]; } +>sampleUser.posts[0] : { title: string; readonly author: { email: string; readonly posts: any[]; }; } +>sampleUser.posts : { title: string; readonly author: { email: string; readonly posts: any[]; }; }[] +>sampleUser : { email: string; readonly posts: { title: string; readonly author: any; }[]; } +>posts : { title: string; readonly author: { email: string; readonly posts: any[]; }; }[] +>0 : 0 +>author : { email: string; readonly posts: { title: string; readonly author: any; }[]; } +>email : string + +// The printer elides a recursive reference as `any`, so a collapsed result prints the same as a +// resolved one. Reading a key neither schema declares is what tells them apart: it is an error here +// and permitted on `any`, so a collapse turns this directive into an unused one. +// @ts-expect-error +sampleUser.missing; +>sampleUser.missing : error +>sampleUser : { email: string; readonly posts: { title: string; readonly author: any; }[]; } +>missing : any + diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterMutual.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterMutual.ts new file mode 100644 index 0000000000000..a27c639456613 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterMutual.ts @@ -0,0 +1,89 @@ +// @strict: true +// @noEmit: true + +// Two schemas that name each other through getters, with the object's members reached through two +// separate key remappings -- one for the output side and one for the input side, each keyed on a +// different member of the schema's own internals. +// +// Both remappings read the member table of a type whose bases are still being inherited, and they +// read it at different points in that assembly. That is what makes this shape the one that separates +// the two answers a miss in that window can have. A speculative comparison has to decline, because +// the member it would force is the one its own question is about. An ordinary lookup has to finish +// itself against the base types still to be inherited, which is what keeps the window unobservable. +// Giving a speculative miss the second answer forces `posts` while its own return type is still +// being inferred, and the getter falls back to an implicit `any` -- so the two cannot be merged into +// one answer, and a single remapping does not reach the case. + +interface Internals { + output: O; + input: I; + optin?: "optional" | undefined; + optout?: "optional" | undefined; +} + +interface Schema { + _zod: Internals; +} + +type output = T["_zod"]["output"]; +type input = T["_zod"]["input"]; + +interface StringSchema extends Schema {} +declare function text(): StringSchema; + +interface ArraySchema extends Schema[], input[]> {} +declare function array(element: T): ArraySchema; + +type OptionalOutSchema = { _zod: { optout: "optional" } }; +type OptionalInSchema = { _zod: { optin: "optional" } }; +type Shape = Readonly<{ [k: string]: Schema }>; +type Prettify = { [K in keyof T]: T[K] } & {}; + +type ObjectOut = Prettify< + { + [k in keyof T as T[k] extends OptionalOutSchema ? never : k]: output; + } & { + [k in keyof T as T[k] extends OptionalOutSchema ? k : never]?: output; + } +>; + +type ObjectIn = Prettify< + { + [k in keyof T as T[k] extends OptionalInSchema ? never : k]: input; + } & { + [k in keyof T as T[k] extends OptionalInSchema ? k : never]?: input; + } +>; + +interface ObjectSchema extends Schema, ObjectIn> { + shape: S; +} +declare function object(shape: T): ObjectSchema; + +const user = object({ + email: text(), + get posts() { + return array(post); + }, +}); + +const post = object({ + title: text(), + get author() { + return user; + }, +}); + +type UserT = output; +declare const sampleUser: UserT; +// Every step of the cycle resolves, including the one that walks back to where it started. On main +// both declarations get an implicit `any` instead. +const email: string = sampleUser.email; +const postTitle: string = sampleUser.posts[0].title; +const authorEmail: string = sampleUser.posts[0].author.email; + +// The printer elides a recursive reference as `any`, so a collapsed result prints the same as a +// resolved one. Reading a key neither schema declares is what tells them apart: it is an error here +// and permitted on `any`, so a collapse turns this directive into an unused one. +// @ts-expect-error +sampleUser.missing; From dd05678ba1b43323cfb3f69e0d4023c0e7bf9ac9 Mon Sep 17 00:00:00 2001 From: Colin McDonnell <3084745+colinhacks@users.noreply.github.com> Date: Fri, 4 Sep 2026 09:28:17 -0700 Subject: [PATCH 7/9] Restore a replaced stack by header, not by length saveStacks recorded the length of each checker stack and restoreStacks re-sliced whatever slice was current. That holds for a stack only ever appended to and truncated, which is eleven of the fourteen. Three are replaced wholesale by a caller that puts the original back only on a normal return: checkExpressionCachedEx swaps in a nil flow-loop stack, getVariancesWorker does the same to the variance stack, and checkSourceFile clears the renamed-binding-elements stack per file. An unwind that crosses one of those leaves the replacement in place, so restoring by length re-slices the wrong slice. Every getter body with a return goes through checkExpressionCachedEx, so a getter forced from inside a loop back-edge -- where the outer flow-loop stack is non-empty -- reached nil[:N]. That is a runtime panic, raised inside the deferred recover, so it is not the sentinel and every outer recover re-panics it: the compiler dies on input it should merely report on. Hold those three as slice headers instead, which is what the flow type cache in the same struct already does. The other eleven keep their lengths, and the tail-clearing that goes with them. --- tsc/internal/checker/checker.go | 49 +++-- ...ThroughObjectLiteralGetterFlowLoop.symbols | 182 ++++++++++++++++++ ...peThroughObjectLiteralGetterFlowLoop.types | 130 +++++++++++++ ...eTypeThroughObjectLiteralGetterFlowLoop.ts | 68 +++++++ 4 files changed, 409 insertions(+), 20 deletions(-) create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.types create mode 100644 tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.ts diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 9b2b42368221d..8f69084575799 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -19131,18 +19131,25 @@ type circularQuestion struct{} // The checker's push/pop state, so an abandoned attempt can be unwound without leaving a push behind. type checkerStacks struct { - typeResolutions, varianceStack, activeMappers, activeTypeMappersCaches int - antecedentTypes, awaitedTypeStack, contextualBindingPatterns int - contextualInfos, flowLoopStack, inferenceContextInfos, sharedFlows int - reverseMappedSourceStack, reverseMappedTargetStack int - renamedBindingElementsInTypes, resolutionStart int - instantiationDepth, conditionalConstraintDepth uint32 - currentNode *ast.Node - varianceTypeParameter *Type - flowTypeCache map[*ast.Node]*Type - reliabilityFlags RelationComparisonResult - reverseExpandingFlags ExpandingFlags - flowAnalysisDisabled, withinUnreachableCode bool + typeResolutions, activeMappers, activeTypeMappersCaches int + antecedentTypes, awaitedTypeStack, contextualBindingPatterns int + contextualInfos, inferenceContextInfos, sharedFlows int + reverseMappedSourceStack, reverseMappedTargetStack, resolutionStart int + instantiationDepth, conditionalConstraintDepth uint32 + // These three are held as slice headers rather than lengths because they are the ones some caller + // replaces wholesale -- checkExpressionCachedEx and getVariancesWorker swap in a nil stack and put + // the old one back only on a normal return, and checkSourceFile clears one per file. A length is + // meaningless against a slice that was swapped out, and restoring one would re-slice the + // replacement rather than the stack that was saved. + varianceStack []VarianceStackEntry + flowLoopStack []FlowLoopInfo + renamedBindingElementsInTypes []*ast.Node + currentNode *ast.Node + varianceTypeParameter *Type + flowTypeCache map[*ast.Node]*Type + reliabilityFlags RelationComparisonResult + reverseExpandingFlags ExpandingFlags + flowAnalysisDisabled, withinUnreachableCode bool } // Whether any base of t could still contribute a member named name. resolveObjectTypeMembers publishes @@ -19182,14 +19189,16 @@ func (c *Checker) mayInheritProperty(t *Type, name string, seen []*Type) bool { func (c *Checker) saveStacks() checkerStacks { return checkerStacks{ - typeResolutions: len(c.typeResolutions), varianceStack: len(c.varianceStack), - activeMappers: len(c.activeMappers), activeTypeMappersCaches: len(c.activeTypeMappersCaches), + typeResolutions: len(c.typeResolutions), + activeMappers: len(c.activeMappers), activeTypeMappersCaches: len(c.activeTypeMappersCaches), antecedentTypes: len(c.antecedentTypes), awaitedTypeStack: len(c.awaitedTypeStack), contextualBindingPatterns: len(c.contextualBindingPatterns), contextualInfos: len(c.contextualInfos), - flowLoopStack: len(c.flowLoopStack), inferenceContextInfos: len(c.inferenceContextInfos), - sharedFlows: len(c.sharedFlows), reverseMappedSourceStack: len(c.reverseMappedSourceStack), + inferenceContextInfos: len(c.inferenceContextInfos), + sharedFlows: len(c.sharedFlows), reverseMappedSourceStack: len(c.reverseMappedSourceStack), reverseMappedTargetStack: len(c.reverseMappedTargetStack), - renamedBindingElementsInTypes: len(c.renamedBindingElementsInTypes), + varianceStack: c.varianceStack, + flowLoopStack: c.flowLoopStack, + renamedBindingElementsInTypes: c.renamedBindingElementsInTypes, resolutionStart: c.resolutionStart, instantiationDepth: c.instantiationDepth, conditionalConstraintDepth: c.conditionalConstraintDepth, currentNode: c.currentNode, varianceTypeParameter: c.varianceTypeParameter, flowTypeCache: c.flowTypeCache, @@ -19201,7 +19210,7 @@ func (c *Checker) saveStacks() checkerStacks { func (c *Checker) restoreStacks(s checkerStacks) { clear(c.typeResolutions[s.typeResolutions:]) c.typeResolutions = c.typeResolutions[:s.typeResolutions] - c.varianceStack = c.varianceStack[:s.varianceStack] + c.varianceStack = s.varianceStack clear(c.activeMappers[s.activeMappers:]) c.activeMappers = c.activeMappers[:s.activeMappers] for _, cache := range c.activeTypeMappersCaches[s.activeTypeMappersCaches:] { @@ -19212,12 +19221,12 @@ func (c *Checker) restoreStacks(s checkerStacks) { c.awaitedTypeStack = c.awaitedTypeStack[:s.awaitedTypeStack] c.contextualBindingPatterns = c.contextualBindingPatterns[:s.contextualBindingPatterns] c.contextualInfos = c.contextualInfos[:s.contextualInfos] - c.flowLoopStack = c.flowLoopStack[:s.flowLoopStack] + c.flowLoopStack = s.flowLoopStack c.inferenceContextInfos = c.inferenceContextInfos[:s.inferenceContextInfos] c.sharedFlows = c.sharedFlows[:s.sharedFlows] c.reverseMappedSourceStack = c.reverseMappedSourceStack[:s.reverseMappedSourceStack] c.reverseMappedTargetStack = c.reverseMappedTargetStack[:s.reverseMappedTargetStack] - c.renamedBindingElementsInTypes = c.renamedBindingElementsInTypes[:s.renamedBindingElementsInTypes] + c.renamedBindingElementsInTypes = s.renamedBindingElementsInTypes c.resolutionStart = s.resolutionStart c.instantiationDepth = s.instantiationDepth c.conditionalConstraintDepth = s.conditionalConstraintDepth diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.symbols new file mode 100644 index 0000000000000..f01a3fabe2b13 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.symbols @@ -0,0 +1,182 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterFlowLoop.ts === +// A recursive getter whose body runs a loop, assigning to a union-typed variable. +// +// The loop puts entries on the flow-loop stack, and the assignment's declared type being a union is +// what makes control flow analysis evaluate the assigned expression rather than take the declared +// type. So the inner getter is forced from inside a loop back-edge, while a provisional comparison is +// open. +// +// This is the case that separates saving a stack's length from saving the stack itself. +// `checkExpressionCachedEx` swaps in a nil flow-loop stack and puts the original back only when it +// returns normally, so an unwind that crosses it leaves the nil in place. Restoring by length then +// re-slices the replacement instead of the saved stack, which is a runtime panic rather than a +// diagnostic. The three stacks some caller replaces wholesale are therefore held as slice headers. + +interface Internals { +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 0, 0)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 20)) + + readonly out: O; +>out : Symbol(Internals.out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 38)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 20)) +} + +interface Schema { +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) + + readonly internals: Internals; +>internals : Symbol(Schema.internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 17, 18)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 0, 0)) +} + +type Out = T["internals"]["out"]; +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 19, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 21, 9)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 21, 9)) + +interface ArrayInternals extends Internals[]> {} +>ArrayInternals : Symbol(ArrayInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 21, 51)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 23, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 0, 0)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 19, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 23, 25)) + +interface ArraySchema extends Schema { +>ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 23, 73)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 24, 22)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) + + readonly internals: ArrayInternals; +>internals : Symbol(ArraySchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 24, 65)) +>ArrayInternals : Symbol(ArrayInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 21, 51)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 24, 22)) +} + +interface StringInternals extends Internals {} +>StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 26, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 0, 0)) + +interface StringSchema extends Schema { +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 28, 54)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) + + readonly internals: StringInternals; +>internals : Symbol(StringSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 29, 39)) +>StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 26, 1)) +} + +type Shape = Readonly<{ [k: string]: Schema }>; +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 31, 1)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 33, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) + +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +>ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 33, 47)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 26)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 31, 1)) +>Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 0, 0)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 64)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 26)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 19, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 26)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 64)) + +interface ObjectSchema extends Schema { +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 94)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 35, 23)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 31, 1)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 31, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) + + readonly internals: ObjectInternals; +>internals : Symbol(ObjectSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 35, 64)) +>ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 33, 47)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 35, 23)) +} + +declare function object(shape: S): ObjectSchema; +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 37, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 39, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 31, 1)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 39, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 39, 24)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 94)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 39, 24)) + +declare function array(element: T): ArraySchema; +>array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 39, 68)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 40, 23)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) +>element : Symbol(element, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 40, 41)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 40, 23)) +>ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 23, 73)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 40, 23)) + +declare function text(): StringSchema; +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 40, 69)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 28, 54)) + +const node = object({ +>node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 43, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 37, 1)) + + name: text(), +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 43, 21)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 40, 69)) + + get children() { +>children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 44, 17)) + + let holder: Schema | null = null; +>holder : Symbol(holder, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 46, 11)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) + + for (let i = 0; i < 2; i++) { +>i : Symbol(i, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 47, 16)) +>i : Symbol(i, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 47, 16)) +>i : Symbol(i, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 47, 16)) + + holder = object({ +>holder : Symbol(holder, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 46, 11)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 37, 1)) + + get inner() { +>inner : Symbol(inner, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 48, 29)) + + return array(node); +>array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 39, 68)) +>node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 43, 5)) + + }, + }); + } + return holder; +>holder : Symbol(holder, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 46, 11)) + + }, +}); + +declare const sample: Out; +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 58, 13)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 19, 1)) +>node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 43, 5)) + +const topName: string = sample.name; +>topName : Symbol(topName, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 59, 5)) +>sample.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 43, 21)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 58, 13)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 43, 21)) + +// A key the shape does not declare is still absent, which is what tells a resolved type apart from +// one that collapsed to `any`. +// @ts-expect-error +sample.missing; +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 58, 13)) + diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.types new file mode 100644 index 0000000000000..931a0adb097bf --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.types @@ -0,0 +1,130 @@ +//// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.ts] //// + +=== recursiveTypeThroughObjectLiteralGetterFlowLoop.ts === +// A recursive getter whose body runs a loop, assigning to a union-typed variable. +// +// The loop puts entries on the flow-loop stack, and the assignment's declared type being a union is +// what makes control flow analysis evaluate the assigned expression rather than take the declared +// type. So the inner getter is forced from inside a loop back-edge, while a provisional comparison is +// open. +// +// This is the case that separates saving a stack's length from saving the stack itself. +// `checkExpressionCachedEx` swaps in a nil flow-loop stack and puts the original back only when it +// returns normally, so an unwind that crosses it leaves the nil in place. Restoring by length then +// re-slices the replacement instead of the saved stack, which is a runtime panic rather than a +// diagnostic. The three stacks some caller replaces wholesale are therefore held as slice headers. + +interface Internals { + readonly out: O; +>out : O +} + +interface Schema { + readonly internals: Internals; +>internals : Internals +} + +type Out = T["internals"]["out"]; +>Out : Out + +interface ArrayInternals extends Internals[]> {} +interface ArraySchema extends Schema { + readonly internals: ArrayInternals; +>internals : ArrayInternals +} + +interface StringInternals extends Internals {} +interface StringSchema extends Schema { + readonly internals: StringInternals; +>internals : StringInternals +} + +type Shape = Readonly<{ [k: string]: Schema }>; +>Shape : Readonly<{ [k: string]: Schema; }> +>k : string + +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +interface ObjectSchema extends Schema { + readonly internals: ObjectInternals; +>internals : ObjectInternals +} + +declare function object(shape: S): ObjectSchema; +>object : (shape: S) => ObjectSchema +>shape : S + +declare function array(element: T): ArraySchema; +>array : (element: T) => ArraySchema +>element : T + +declare function text(): StringSchema; +>text : () => StringSchema + +const node = object({ +>node : ObjectSchema<{ name: StringSchema; readonly children: Schema | null; }> +>object({ name: text(), get children() { let holder: Schema | null = null; for (let i = 0; i < 2; i++) { holder = object({ get inner() { return array(node); }, }); } return holder; },}) : ObjectSchema<{ name: StringSchema; readonly children: Schema | null; }> +>object : (shape: S) => ObjectSchema +>{ name: text(), get children() { let holder: Schema | null = null; for (let i = 0; i < 2; i++) { holder = object({ get inner() { return array(node); }, }); } return holder; },} : { name: StringSchema; readonly children: Schema | null; } + + name: text(), +>name : StringSchema +>text() : StringSchema +>text : () => StringSchema + + get children() { +>children : Schema | null + + let holder: Schema | null = null; +>holder : Schema | null + + for (let i = 0; i < 2; i++) { +>i : number +>0 : 0 +>i < 2 : boolean +>i : number +>2 : 2 +>i++ : number +>i : number + + holder = object({ +>holder = object({ get inner() { return array(node); }, }) : ObjectSchema<{ readonly inner: ArraySchema>; }> +>holder : Schema | null +>object({ get inner() { return array(node); }, }) : ObjectSchema<{ readonly inner: ArraySchema>; }> +>object : (shape: S) => ObjectSchema +>{ get inner() { return array(node); }, } : { readonly inner: ArraySchema>; } + + get inner() { +>inner : ArraySchema> + + return array(node); +>array(node) : ArraySchema> +>array : (element: T) => ArraySchema +>node : ObjectSchema<{ name: StringSchema; readonly children: Schema | null; }> + + }, + }); + } + return holder; +>holder : Schema | null + + }, +}); + +declare const sample: Out; +>sample : { name: string; readonly children: unknown; } +>node : ObjectSchema<{ name: StringSchema; readonly children: Schema | null; }> + +const topName: string = sample.name; +>topName : string +>sample.name : string +>sample : { name: string; readonly children: unknown; } +>name : string + +// A key the shape does not declare is still absent, which is what tells a resolved type apart from +// one that collapsed to `any`. +// @ts-expect-error +sample.missing; +>sample.missing : error +>sample : { name: string; readonly children: unknown; } +>missing : any + diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.ts new file mode 100644 index 0000000000000..7b5cba6d5d01d --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.ts @@ -0,0 +1,68 @@ +// @strict: true +// @noEmit: true + +// A recursive getter whose body runs a loop, assigning to a union-typed variable. +// +// The loop puts entries on the flow-loop stack, and the assignment's declared type being a union is +// what makes control flow analysis evaluate the assigned expression rather than take the declared +// type. So the inner getter is forced from inside a loop back-edge, while a provisional comparison is +// open. +// +// This is the case that separates saving a stack's length from saving the stack itself. +// `checkExpressionCachedEx` swaps in a nil flow-loop stack and puts the original back only when it +// returns normally, so an unwind that crosses it leaves the nil in place. Restoring by length then +// re-slices the replacement instead of the saved stack, which is a runtime panic rather than a +// diagnostic. The three stacks some caller replaces wholesale are therefore held as slice headers. + +interface Internals { + readonly out: O; +} + +interface Schema { + readonly internals: Internals; +} + +type Out = T["internals"]["out"]; + +interface ArrayInternals extends Internals[]> {} +interface ArraySchema extends Schema { + readonly internals: ArrayInternals; +} + +interface StringInternals extends Internals {} +interface StringSchema extends Schema { + readonly internals: StringInternals; +} + +type Shape = Readonly<{ [k: string]: Schema }>; +interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} +interface ObjectSchema extends Schema { + readonly internals: ObjectInternals; +} + +declare function object(shape: S): ObjectSchema; +declare function array(element: T): ArraySchema; +declare function text(): StringSchema; + +const node = object({ + name: text(), + get children() { + let holder: Schema | null = null; + for (let i = 0; i < 2; i++) { + holder = object({ + get inner() { + return array(node); + }, + }); + } + return holder; + }, +}); + +declare const sample: Out; +const topName: string = sample.name; + +// A key the shape does not declare is still absent, which is what tells a resolved type apart from +// one that collapsed to `any`. +// @ts-expect-error +sample.missing; From 953cfe8e109d0f2bf345142ab426fd919f4c5ad7 Mon Sep 17 00:00:00 2001 From: Colin McDonnell <3084745+colinhacks@users.noreply.github.com> Date: Fri, 4 Sep 2026 09:28:18 -0700 Subject: [PATCH 8/9] Say what the inherited-member case actually does Its header still said "This case is NOT fixed" and "The recursive one does not resolve", which was true of an earlier draft and has not been true since the lookup started completing itself against the bases still to be inherited. The `any` printed against `parent` in the .types baseline is the printer eliding a recursive reference, not an unresolved member, so nothing in the baseline contradicted the stale prose and it survived. Add the assertions that would have caught it: the name three levels down is a string, a number annotation on it is an error, and a key the shape does not declare is absent at depth. All three are permitted on `any`, so a collapse turns them into unused-directive errors rather than passing quietly. --- ...ObjectLiteralGetterInheritedMember.symbols | 85 +++++++++++++------ ...ghObjectLiteralGetterInheritedMember.types | 62 +++++++++++--- ...roughObjectLiteralGetterInheritedMember.ts | 27 +++--- 3 files changed, 128 insertions(+), 46 deletions(-) diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols index 527542b1c3c08..f28ec5e547b85 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols @@ -6,16 +6,16 @@ // its base with a type that names the schema back, so the schema's table is read while it is still // being assembled and its inherited `out` is not in it yet. // -// This case is NOT fixed, and the baseline records how far it gets. An earlier draft answered every -// miss in that window with `any`, which cleared the error here -- but that answer is not confined to -// this shape. A self-referential interface reaches the same window with no getter and no inference -// anywhere in the program, and an unpatched compiler answers it from the index signature; making it -// `any` silently accepted an indexed access that every other compiler rejects. Nothing available at -// the miss distinguishes the two, so the answer was withdrawn rather than guessed. +// A lookup that misses in that window finishes itself against the base types still to be inherited, +// so it returns the member the table is about to hold. An earlier draft instead answered such a miss +// with `any`, which cleared the error here and was wrong everywhere else: a self-referential +// interface reaches the same window with no getter and no inference anywhere in the program, and an +// unpatched compiler answers it from the index signature, so `any` silently accepted an indexed +// access that every other compiler rejects. Completing the lookup is what separates the two -- a name +// no base carries is still absent. // -// What is left is still an improvement on main, which cannot type the declaration at all: the -// getter resolves, the non-recursive members are real types, and only the remapped member is -// unresolved. +// The `any` that appears against `parent` in the .types baseline is the printer eliding a recursive +// reference, not an unresolved member. The assertions below are what distinguish those two. interface Internals { >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 0, 0)) @@ -154,7 +154,7 @@ const topName: string = sample.name; >sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) >name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) -// The recursive one does not resolve. Pinned so that closing this shows up here. +// The recursive one resolves too, and keeps resolving as far down as it is walked. const parentName: string = sample.parent!.name; >parentName : Symbol(parentName, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 60, 5)) >sample.parent!.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) @@ -163,33 +163,66 @@ const parentName: string = sample.parent!.name; >parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) >name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) +const deepName: string = sample.parent!.parent!.parent!.name; +>deepName : Symbol(deepName, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 61, 5)) +>sample.parent!.parent!.parent!.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) +>sample.parent!.parent!.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>sample.parent!.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>sample.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) + +// Depth is where a collapse would hide: `any` would accept both of these, so they are what keeps the +// comment above honest. +// @ts-expect-error +const wrongType: number = sample.parent!.parent!.name; +>wrongType : Symbol(wrongType, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 5)) +>sample.parent!.parent!.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) +>sample.parent!.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>sample.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) + +// @ts-expect-error +sample.parent!.parent!.missing; +>sample.parent!.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>sample.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) + // A property that is genuinely absent must still be reported, whoever is asking. interface Base { readonly own: number; } ->Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 60, 47)) ->own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 16)) +>Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 67, 31)) +>own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 70, 16)) interface Derived extends Base { readonly extra: string; } ->Derived : Symbol(Derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 40)) ->Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 60, 47)) ->extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 64, 32)) +>Derived : Symbol(Derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 70, 40)) +>Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 67, 31)) +>extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 71, 32)) declare const derived: Derived; ->derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 13)) ->Derived : Symbol(Derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 40)) +>derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 72, 13)) +>Derived : Symbol(Derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 70, 40)) // @ts-expect-error derived.missing; ->derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 13)) +>derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 72, 13)) const ownValue: number = derived.own; ->ownValue : Symbol(ownValue, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 68, 5)) ->derived.own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 16)) ->derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 13)) ->own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 16)) +>ownValue : Symbol(ownValue, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 75, 5)) +>derived.own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 70, 16)) +>derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 72, 13)) +>own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 70, 16)) const extraValue: string = derived.extra; ->extraValue : Symbol(extraValue, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 69, 5)) ->derived.extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 64, 32)) ->derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 13)) ->extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 64, 32)) +>extraValue : Symbol(extraValue, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 76, 5)) +>derived.extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 71, 32)) +>derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 72, 13)) +>extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 71, 32)) diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types index 81bc7984cb74e..57b18960ce823 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types @@ -6,16 +6,16 @@ // its base with a type that names the schema back, so the schema's table is read while it is still // being assembled and its inherited `out` is not in it yet. // -// This case is NOT fixed, and the baseline records how far it gets. An earlier draft answered every -// miss in that window with `any`, which cleared the error here -- but that answer is not confined to -// this shape. A self-referential interface reaches the same window with no getter and no inference -// anywhere in the program, and an unpatched compiler answers it from the index signature; making it -// `any` silently accepted an indexed access that every other compiler rejects. Nothing available at -// the miss distinguishes the two, so the answer was withdrawn rather than guessed. +// A lookup that misses in that window finishes itself against the base types still to be inherited, +// so it returns the member the table is about to hold. An earlier draft instead answered such a miss +// with `any`, which cleared the error here and was wrong everywhere else: a self-referential +// interface reaches the same window with no getter and no inference anywhere in the program, and an +// unpatched compiler answers it from the index signature, so `any` silently accepted an indexed +// access that every other compiler rejects. Completing the lookup is what separates the two -- a name +// no base carries is still absent. // -// What is left is still an improvement on main, which cannot type the declaration at all: the -// getter resolves, the non-recursive members are real types, and only the remapped member is -// unresolved. +// The `any` that appears against `parent` in the .types baseline is the printer eliding a recursive +// reference, not an unresolved member. The assertions below are what distinguish those two. interface Internals { optional: "true" | "false"; @@ -104,7 +104,7 @@ const topName: string = sample.name; >sample : { name: string; readonly parent?: { name: string; readonly parent?: any | undefined; } | undefined; } >name : string -// The recursive one does not resolve. Pinned so that closing this shows up here. +// The recursive one resolves too, and keeps resolving as far down as it is walked. const parentName: string = sample.parent!.name; >parentName : string >sample.parent!.name : string @@ -114,6 +114,48 @@ const parentName: string = sample.parent!.name; >parent : { name: string; readonly parent?: any | undefined; } | undefined >name : string +const deepName: string = sample.parent!.parent!.parent!.name; +>deepName : string +>sample.parent!.parent!.parent!.name : string +>sample.parent!.parent!.parent! : { name: string; readonly parent?: any | undefined; } +>sample.parent!.parent!.parent : { name: string; readonly parent?: any | undefined; } | undefined +>sample.parent!.parent! : { name: string; readonly parent?: any | undefined; } +>sample.parent!.parent : { name: string; readonly parent?: any | undefined; } | undefined +>sample.parent! : { name: string; readonly parent?: any | undefined; } +>sample.parent : { name: string; readonly parent?: any | undefined; } | undefined +>sample : { name: string; readonly parent?: { name: string; readonly parent?: any | undefined; } | undefined; } +>parent : { name: string; readonly parent?: any | undefined; } | undefined +>parent : { name: string; readonly parent?: any | undefined; } | undefined +>parent : { name: string; readonly parent?: any | undefined; } | undefined +>name : string + +// Depth is where a collapse would hide: `any` would accept both of these, so they are what keeps the +// comment above honest. +// @ts-expect-error +const wrongType: number = sample.parent!.parent!.name; +>wrongType : number +>sample.parent!.parent!.name : string +>sample.parent!.parent! : { name: string; readonly parent?: any | undefined; } +>sample.parent!.parent : { name: string; readonly parent?: any | undefined; } | undefined +>sample.parent! : { name: string; readonly parent?: any | undefined; } +>sample.parent : { name: string; readonly parent?: any | undefined; } | undefined +>sample : { name: string; readonly parent?: { name: string; readonly parent?: any | undefined; } | undefined; } +>parent : { name: string; readonly parent?: any | undefined; } | undefined +>parent : { name: string; readonly parent?: any | undefined; } | undefined +>name : string + +// @ts-expect-error +sample.parent!.parent!.missing; +>sample.parent!.parent!.missing : error +>sample.parent!.parent! : { name: string; readonly parent?: any | undefined; } +>sample.parent!.parent : { name: string; readonly parent?: any | undefined; } | undefined +>sample.parent! : { name: string; readonly parent?: any | undefined; } +>sample.parent : { name: string; readonly parent?: any | undefined; } | undefined +>sample : { name: string; readonly parent?: { name: string; readonly parent?: any | undefined; } | undefined; } +>parent : { name: string; readonly parent?: any | undefined; } | undefined +>parent : { name: string; readonly parent?: any | undefined; } | undefined +>missing : any + // A property that is genuinely absent must still be reported, whoever is asking. interface Base { readonly own: number; } >own : number diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts index 398155d5947f6..01b91cb73e63f 100644 --- a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts @@ -6,16 +6,16 @@ // its base with a type that names the schema back, so the schema's table is read while it is still // being assembled and its inherited `out` is not in it yet. // -// This case is NOT fixed, and the baseline records how far it gets. An earlier draft answered every -// miss in that window with `any`, which cleared the error here -- but that answer is not confined to -// this shape. A self-referential interface reaches the same window with no getter and no inference -// anywhere in the program, and an unpatched compiler answers it from the index signature; making it -// `any` silently accepted an indexed access that every other compiler rejects. Nothing available at -// the miss distinguishes the two, so the answer was withdrawn rather than guessed. +// A lookup that misses in that window finishes itself against the base types still to be inherited, +// so it returns the member the table is about to hold. An earlier draft instead answered such a miss +// with `any`, which cleared the error here and was wrong everywhere else: a self-referential +// interface reaches the same window with no getter and no inference anywhere in the program, and an +// unpatched compiler answers it from the index signature, so `any` silently accepted an indexed +// access that every other compiler rejects. Completing the lookup is what separates the two -- a name +// no base carries is still absent. // -// What is left is still an improvement on main, which cannot type the declaration at all: the -// getter resolves, the non-recursive members are real types, and only the remapped member is -// unresolved. +// The `any` that appears against `parent` in the .types baseline is the printer eliding a recursive +// reference, not an unresolved member. The assertions below are what distinguish those two. interface Internals { optional: "true" | "false"; @@ -60,8 +60,15 @@ declare const sample: (typeof category)["out"]; // optional bucket, so it is `parent?:` rather than `parent:`. Both of those are new: main gives the // whole declaration an implicit `any` and reports it. const topName: string = sample.name; -// The recursive one does not resolve. Pinned so that closing this shows up here. +// The recursive one resolves too, and keeps resolving as far down as it is walked. const parentName: string = sample.parent!.name; +const deepName: string = sample.parent!.parent!.parent!.name; +// Depth is where a collapse would hide: `any` would accept both of these, so they are what keeps the +// comment above honest. +// @ts-expect-error +const wrongType: number = sample.parent!.parent!.name; +// @ts-expect-error +sample.parent!.parent!.missing; // A property that is genuinely absent must still be reported, whoever is asking. interface Base { readonly own: number; } From 94fd69ce606d8c0608bc94033be2d5c47ebc4b50 Mon Sep 17 00:00:00 2001 From: Colin McDonnell <3084745+colinhacks@users.noreply.github.com> Date: Fri, 4 Sep 2026 12:49:44 -0700 Subject: [PATCH 9/9] Tighten the wording in the recursive-getter comments Comments and test prose only. The compiler diff here is comments alone, and no behaviour changes. The explanations had grown into restating themselves, with asides that do not earn their line and constructions that name what a sentence is doing instead of saying it. Cut those back to what a reader of this code needs. The conformance case headers had the same problem, several of them explaining the type system rather than the case at hand. Baselines move only where a comment shifted a line number. --- tsc/internal/checker/checker.go | 109 +++--- tsc/internal/checker/inference.go | 8 +- tsc/internal/checker/relater.go | 13 +- ...siveTypeThroughObjectLiteralGetter.symbols | 6 +- ...ursiveTypeThroughObjectLiteralGetter.types | 6 +- ...eralGetterAbsentDuringInference.errors.txt | 20 +- ...LiteralGetterAbsentDuringInference.symbols | 94 +++-- ...ctLiteralGetterAbsentDuringInference.types | 18 +- ...ghObjectLiteralGetterConstraint.errors.txt | 4 +- ...roughObjectLiteralGetterConstraint.symbols | 4 +- ...ThroughObjectLiteralGetterConstraint.types | 4 +- ...ThroughObjectLiteralGetterFlowLoop.symbols | 191 +++++----- ...peThroughObjectLiteralGetterFlowLoop.types | 19 +- ...ObjectLiteralGetterInheritedMember.symbols | 267 +++++++------- ...ghObjectLiteralGetterInheritedMember.types | 33 +- ...peThroughObjectLiteralGetterMutual.symbols | 331 +++++++++--------- ...TypeThroughObjectLiteralGetterMutual.types | 19 +- ...hroughObjectLiteralGetterOverloads.symbols | 83 +++-- ...eThroughObjectLiteralGetterOverloads.types | 5 +- ...bjectLiteralGetterReverseMapped.errors.txt | 7 +- ...ghObjectLiteralGetterReverseMapped.symbols | 147 ++++---- ...oughObjectLiteralGetterReverseMapped.types | 3 +- ...ypeThroughObjectLiteralGetterUnion.symbols | 6 +- ...eTypeThroughObjectLiteralGetterUnion.types | 6 +- ...ghObjectLiteralGetterUnionOptional.symbols | 2 +- ...oughObjectLiteralGetterUnionOptional.types | 2 +- ...roughObjectLiteralGetterVariations.symbols | 4 +- ...ThroughObjectLiteralGetterVariations.types | 4 +- ...recursiveTypeThroughObjectLiteralGetter.ts | 6 +- ...bjectLiteralGetterAbsentDuringInference.ts | 18 +- ...ypeThroughObjectLiteralGetterConstraint.ts | 4 +- ...eTypeThroughObjectLiteralGetterFlowLoop.ts | 19 +- ...roughObjectLiteralGetterInheritedMember.ts | 33 +- ...iveTypeThroughObjectLiteralGetterMutual.ts | 19 +- ...TypeThroughObjectLiteralGetterOverloads.ts | 5 +- ...ThroughObjectLiteralGetterReverseMapped.ts | 3 +- ...siveTypeThroughObjectLiteralGetterUnion.ts | 6 +- ...ThroughObjectLiteralGetterUnionOptional.ts | 2 +- ...ypeThroughObjectLiteralGetterVariations.ts | 4 +- 39 files changed, 732 insertions(+), 802 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 8f69084575799..32d09e14cf27d 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -2547,18 +2547,17 @@ func (c *Checker) checkDeferredNodes(context *ast.SourceFile) { c.recheckSkippedMembers(context) } -// A comparison that passed over a member did not verify the constraint on it, it postponed it. This is -// where the postponement is honoured: by now the declarations involved have types of their own, so the -// member is compared against the same target, this time reporting. Without it a getter whose type does -// not satisfy the constraint is simply accepted. +// Honours the constraint checks compareProvisionally postponed. By now the declarations involved have +// types, so each recorded member is compared against the same target, this time reporting. Without this +// a getter whose type violates the constraint is accepted. func (c *Checker) recheckSkippedMembers(context *ast.SourceFile) { pending := c.skippedMemberChecks c.skippedMemberChecks = nil c.recheckingSkippedMembers = true defer func() { c.recheckingSkippedMembers = false }() for _, check := range pending { - // An obligation is honoured in the pass for the file its error belongs to, so the diagnostic lands - // with that file. Where that file has been checked already there is no later pass to wait for. + // Report in the pass for the file the error belongs to. If that file is already checked there is no + // later pass to defer to. file := ast.GetSourceFileOfNode(check.errorNode) if file != context && !c.sourceFileLinks.Get(file).typeChecked { c.skippedMemberChecks = append(c.skippedMemberChecks, check) @@ -2568,16 +2567,15 @@ func (c *Checker) recheckSkippedMembers(context *ast.SourceFile) { } } -// Record a member the constraint check could not resolve, so it can be compared once it has a type. -// Dropping it instead would leave nothing recording that the comparison never happened, which is how an -// invalid member gets in. +// Records a member the constraint check could not resolve, to be compared once it has a type. Dropping +// it would leave the comparison unrecorded, which admits an invalid member. func (c *Checker) postponeMemberCheck(prop *ast.Symbol, target *Type, relation *Relation) { if c.recheckingSkippedMembers { return } - // The accessor's own declaration, not the relater's error node: that one is whatever the outermost - // comparison was handed, so taking it would report one defect at different places depending on which - // sub-comparison happened to skip the member. + // Use the accessor's own declaration rather than the relater's error node, which is whatever the + // outermost comparison was handed and would move one defect between locations depending on which + // sub-comparison skipped the member. var where *ast.Node for _, declaration := range prop.Declarations { if declaration == nil { @@ -19116,7 +19114,7 @@ func (c *Checker) getCombinedModifierFlagsCached(node *ast.Node) ast.ModifierFla return c.lastGetCombinedModifierFlagsResult } -// A constraint comparison postponed because the member could not be worked out yet. +// A constraint comparison postponed because the member had no type yet. type skippedMemberCheck struct { property *ast.Symbol target *Type @@ -19125,22 +19123,20 @@ type skippedMemberCheck struct { } // Raised when a resolution started by a provisional question re-enters a declaration that was already -// being resolved when that question was asked. Nothing computed from the circular value is allowed to -// complete, so no diagnostic is reported and no cache records it. +// being resolved when the question was asked. Nothing computed from the value completes, so nothing is +// reported and nothing is cached. type circularQuestion struct{} -// The checker's push/pop state, so an abandoned attempt can be unwound without leaving a push behind. +// Checker push/pop state, so an abandoned attempt unwinds without leaving a push behind. type checkerStacks struct { typeResolutions, activeMappers, activeTypeMappersCaches int antecedentTypes, awaitedTypeStack, contextualBindingPatterns int contextualInfos, inferenceContextInfos, sharedFlows int reverseMappedSourceStack, reverseMappedTargetStack, resolutionStart int instantiationDepth, conditionalConstraintDepth uint32 - // These three are held as slice headers rather than lengths because they are the ones some caller - // replaces wholesale -- checkExpressionCachedEx and getVariancesWorker swap in a nil stack and put - // the old one back only on a normal return, and checkSourceFile clears one per file. A length is - // meaningless against a slice that was swapped out, and restoring one would re-slice the - // replacement rather than the stack that was saved. + // Held as slice headers rather than lengths. checkExpressionCachedEx and getVariancesWorker swap in a + // nil stack and restore it only on a normal return, and checkSourceFile clears one per file, so a saved + // length would re-slice the replacement instead of the stack that was saved. varianceStack []VarianceStackEntry flowLoopStack []FlowLoopInfo renamedBindingElementsInTypes []*ast.Node @@ -19152,10 +19148,9 @@ type checkerStacks struct { flowAnalysisDisabled, withinUnreachableCode bool } -// Whether any base of t could still contribute a member named name. resolveObjectTypeMembers publishes -// the self-declared table before it walks the bases, so a miss inside that window is "not known yet" -// rather than "absent" -- but only for a name a base actually declares. Reading declaration tables -// answers that without forcing a single type. +// Whether any base of t could still contribute a member named name. Inside the window opened by +// resolveObjectTypeMembers a miss means "not yet" rather than "absent", but only for a name a base +// declares. Answered from declaration tables, forcing no types. func (c *Checker) mayInheritProperty(t *Type, name string, seen []*Type) bool { declared := t if declared.objectFlags&ObjectFlagsReference != 0 { @@ -19239,9 +19234,9 @@ func (c *Checker) restoreStacks(s checkerStacks) { c.withinUnreachableCode = s.withinUnreachableCode } -// Run a comparison that reports nothing and only decides whether to keep an inferred candidate. Inside -// it a member whose type cannot be computed yet is passed over rather than forced -- see -// tryGetTypeOfMember -- and unresolvableMembers counts how many were. +// Runs a comparison that reports nothing and only decides whether to keep an inferred candidate. A +// member whose type cannot be computed yet is passed over rather than forced, see tryGetTypeOfMember, +// and unresolvableMembers counts them. func (c *Checker) compareProvisionally(compare func() Ternary) (result Ternary, answeredInFull bool) { savedMark, savedDepth := c.provisionalMark, c.provisionalDepth stacks := c.saveStacks() @@ -19251,8 +19246,8 @@ func (c *Checker) compareProvisionally(compare func() Ternary) (result Ternary, defer func() { c.provisionalDepth, c.provisionalMark = savedDepth, savedMark if r := recover(); r != nil { - // A member forced somewhere other than tryGetTypeOfMember, so nothing nearer caught it. The - // comparison is over either way, and it answered for less than the whole type. + // Forced somewhere other than tryGetTypeOfMember, so nothing nearer caught it. The comparison is over + // either way and it answered for less than the whole type. if _, ours := r.(circularQuestion); !ours { panic(r) } @@ -19265,12 +19260,10 @@ func (c *Checker) compareProvisionally(compare func() Ternary) (result Ternary, return result, c.unresolvableMembers == skippedBefore } -// Get a member's type for a provisional comparison, or report that it has none to give yet. A getter is -// how a self-referential object literal is written and the one member kind whose type is computed -// lazily, so forcing one here routinely re-enters the declaration the comparison is being made for. The -// attempt is made rather than predicted -- a member is only passed over once it has proved unanswerable -// -- and abandoning it leaves nothing behind, because nothing computed from the circular value -// completes. +// Returns a member's type for a provisional comparison, or reports that it has none yet. Getters are the +// one member kind computed lazily, so forcing one here re-enters the declaration being compared. The +// attempt is made rather than predicted: a member is passed over only once it has proved unanswerable, +// and abandoning leaves nothing behind because nothing computed from the value completes. func (c *Checker) tryGetTypeOfMember(get func(*ast.Symbol) *Type, symbol *ast.Symbol) (t *Type, resolved bool) { if c.provisionalDepth == 0 { return get(symbol), true @@ -19304,9 +19297,9 @@ func (c *Checker) pushTypeResolution(target TypeSystemEntity, propertyName TypeS resolutionCycleStartIndex := c.findResolutionCycleStartIndex(target, propertyName) if resolutionCycleStartIndex >= 0 { if c.provisionalDepth != 0 && resolutionCycleStartIndex < c.provisionalMark { - // The cycle runs back through a declaration that was already being resolved when the - // provisional question was asked, so the question caused it. Abandon the attempt instead - // of answering `any`: a verdict that decides nothing must not fix a declaration's type. + // The cycle runs back through a declaration already being resolved when the provisional question was + // asked, so the question caused it. Abandon rather than answering `any`, which would fix a declaration's + // type from a comparison that decides nothing. panic(circularQuestion{}) } // A cycle was found @@ -19616,9 +19609,9 @@ func (c *Checker) isApplicableIndexType(source *Type, target *Type) bool { func (c *Checker) resolveStructuredTypeMembers(t *Type) *StructuredType { if t.objectFlags&ObjectFlagsMembersResolved == 0 { if c.provisionalDepth != 0 { - // resolveObjectTypeMembers publishes a partial table as its own recursion guard and completes - // it afterwards, so an attempt abandoned in that window would leave a type holding only what it - // declares itself, marked resolved. Put it back to unresolved so the next reader recomputes it. + // resolveObjectTypeMembers publishes a partial table as a recursion guard and completes it afterwards, + // so an attempt abandoned in that window would leave a type marked resolved while holding only its own + // members. Reset it to unresolved so the next reader recomputes. defer func() { if r := recover(); r != nil { t.objectFlags &^= ObjectFlagsMembersResolved | ObjectFlagsUnresolvedMembers @@ -19697,9 +19690,8 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) thisArgument := core.LastOrNil(typeArguments) t.objectFlags |= ObjectFlagsUnresolvedMembers - // The table just published holds only what the type declares itself, so every inherited member - // reads as absent until the loop below adds it. Recording what is still to come lets a lookup in - // that window finish itself against the bases instead, so the window is never observable. + // The published table holds only the type's own members until the loop below adds the inherited ones. + // Recording what is still to come lets a lookup in that window resolve against the bases instead. c.inheritanceInFlight = append(c.inheritanceInFlight, pendingInheritance{t: t, mapper: mapper, thisArgument: thisArgument, baseTypes: baseTypes}) for _, baseType := range baseTypes { instantiatedBaseType := c.instantiateBaseType(baseType, mapper, thisArgument) @@ -19722,9 +19714,8 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) } -// What resolveObjectTypeMembers has left to inherit, for as long as ObjectFlagsUnresolvedMembers is set. -// These nest strictly, so they are a stack rather than a map -- one that reaches its depth early and -// allocates nothing thereafter. +// What resolveObjectTypeMembers has left to inherit, while ObjectFlagsUnresolvedMembers is set. These +// nest strictly, so a stack rather than a map. It reaches its depth early and allocates nothing after. type pendingInheritance struct { t *Type mapper *TypeMapper @@ -19740,19 +19731,18 @@ func (c *Checker) instantiateBaseType(baseType *Type, mapper *TypeMapper, thisAr return c.getTypeWithThisArgument(c.instantiateType(baseType, mapper), thisArgument, false /*needsApparentType*/) } -// Answers a property lookup that missed on a table whose inherited members have not been added yet, by -// asking the base types the same question. The member it finds is the one the table is about to hold, -// so this is the completed lookup rather than a guess about it -- a name no base carries is still -// absent, and callers need no notion of a provisional answer. +// Answers a lookup that missed on a table whose inherited members are not added yet, by asking the base +// types. The member returned is the one the table is about to hold, so callers need no notion of a +// provisional answer, and a name no base carries is still absent. func (c *Checker) getPendingInheritedProperty(t *Type, name string) *ast.Symbol { i := len(c.inheritanceInFlight) - 1 for i >= 0 && c.inheritanceInFlight[i].t != t { i-- } - // A base that reaches back through t must find the window closed, or the two would ask each other - // forever; marked as being consulted, t answers from its published table instead, which is what the - // loop's own re-entry sees. The index outlives a reallocation of the stack where a pointer into it - // would not, and stays valid because everything pushed above it is popped before this returns. + // A base reaching back through t must find the window closed, or the two recurse. Marked as consulting, + // t answers from its published table, which is what the inheritance loop's own re-entry sees. The index + // survives a reallocation of the stack where a pointer into it would not, and stays valid because + // everything pushed above it is popped before this returns. if i < 0 || c.inheritanceInFlight[i].consulting { return nil } @@ -28114,10 +28104,9 @@ func (c *Checker) getResolvedBaseConstraint(t *Type, stack []RecursionId) *Type } circular := false if !c.popTypeResolution() { - // A provisional comparison reports nothing and decides nothing, so a circularity it runs into is - // the question's own and neither reports nor sticks. It explores further than an ordinary check - // does -- that is the point of it -- and finding a cycle out there says nothing about the program. - // Declining the cache leaves the next ask, outside any region, to reach and report a real one. + // A provisional comparison reports and decides nothing, so a circularity it runs into is the question's + // own. It explores further than an ordinary check, so a cycle found out there says nothing about the + // program. Declining the cache leaves the next ask, outside any region, to reach and report a real one. if t.flags&TypeFlagsTypeParameter != 0 && c.provisionalDepth == 0 { errorNode := c.getConstraintDeclaration(t) if errorNode != nil { diff --git a/tsc/internal/checker/inference.go b/tsc/internal/checker/inference.go index c5c1fb6ae1610..1ed2d7b37936c 100644 --- a/tsc/internal/checker/inference.go +++ b/tsc/internal/checker/inference.go @@ -1380,11 +1380,9 @@ func (c *Checker) getInferredType(n *InferenceContext, index int) *Type { instantiatedConstraint := c.instantiateType(constraint, n.nonFixingMapper) if inferredType != nil { constraintWithThis := c.getTypeWithThisArgument(instantiatedConstraint, inferredType, false) - // Verifying a candidate against its constraint reports nothing and only decides whether to keep - // the candidate, so it must not force a member whose type is still being computed: an object - // literal argument routinely names the very declaration being resolved. A comparison that passed - // over such a member answered for less than the whole type, so it may keep the candidate but - // never reject it. + // Constraint verification must not force a member whose type is still being computed, since an object + // literal argument routinely names the declaration being resolved. A comparison that passed over such a + // member answered for less than the whole type, so it may keep the candidate but never reject it. comparison, answeredInFull := c.compareProvisionally(func() Ternary { return n.compareTypes(inferredType, constraintWithThis, false) }) diff --git a/tsc/internal/checker/relater.go b/tsc/internal/checker/relater.go index 64b08231ecf37..c1747a61f8f00 100644 --- a/tsc/internal/checker/relater.go +++ b/tsc/internal/checker/relater.go @@ -3179,10 +3179,8 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError if result != TernaryFalse { if result == TernaryTrue || (len(r.sourceStack) == 0 && len(r.targetStack) == 0) { if (result == TernaryTrue || result == TernaryMaybe) && r.c.unresolvableMembers == skipsBefore { - // If result is definitely true, record all maybe keys as having succeeded. Also, record Ternary.Maybe - // results as having succeeded once we reach depth 0, but never record Ternary.Unknown results. - // A comparison that passed over a member it could not resolve answered for less than the whole - // type, and the relation cache is global, so the next question must ask again rather than read it. + // This comparison answered for less than the whole type and the relation cache is global, so the next + // question must ask again rather than read it. r.resetMaybeStack(maybeStart, propagatingVarianceFlags, true) } else { r.resetMaybeStack(maybeStart, propagatingVarianceFlags, false) @@ -4266,10 +4264,9 @@ func (r *Relater) propertiesRelatedTo(source *Type, target *Type, reportErrors b } requireOptionalProperties := (r.relation == r.c.subtypeRelation || r.relation == r.c.strictSubtypeRelation) && !isObjectLiteralType(source) && !r.c.isEmptyArrayLiteralType(source) && !isTupleType(source) unmatchedProperty := r.c.getUnmatchedProperty(source, target, requireOptionalProperties, false /*matchDiscriminantProperties*/) - // A miss while the source's member table is mid-assembly means "not known yet", not "absent" -- but - // only inside a provisional region, where the verdict is kept rather than reported, and only for a - // name some base declares. Suppressing it during an ordinary check makes a genuinely absent property - // look present, which is enough to send a conditional type down the wrong branch. + // A miss while the source's table is mid-assembly means "not yet" rather than "absent", but only inside + // a provisional region, and only for a name some base declares. Suppressing it during an ordinary check + // makes an absent property look present, which can send a conditional type down the wrong branch. if unmatchedProperty != nil && r.c.provisionalDepth != 0 && source.objectFlags&ObjectFlagsUnresolvedMembers != 0 && r.c.mayInheritProperty(source, unmatchedProperty.Name, nil) { unmatchedProperty = nil diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.symbols index a7ff11eba5926..ff78a6214dc40 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.symbols +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.symbols @@ -155,9 +155,9 @@ const nestedName: string = sample.children[0].children[0].name; >children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 32, 17)) >name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 31, 21)) -// Those two pass on their own if the whole thing collapsed to `any`, which is exactly the failure -// this test exists to catch, so the recursion point is also probed where `any` cannot hide: an -// unknown key has to be an error, and a known one has to have the type it is declared with. +// Both assertions above pass if the whole thing collapsed to `any`, so the recursion point is also +// probed where `any` cannot hide. An unknown key must be an error, and a known one must carry its +// declared type. // @ts-expect-error sample.children[0].missing; >sample.children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetter.ts, 32, 17)) diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.types index c8b4921b1cbc9..92f95ef1f8e26 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.types +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetter.types @@ -98,9 +98,9 @@ const nestedName: string = sample.children[0].children[0].name; >0 : 0 >name : string -// Those two pass on their own if the whole thing collapsed to `any`, which is exactly the failure -// this test exists to catch, so the recursion point is also probed where `any` cannot hide: an -// unknown key has to be an error, and a known one has to have the type it is declared with. +// Both assertions above pass if the whole thing collapsed to `any`, so the recursion point is also +// probed where `any` cannot hide. An unknown key must be an error, and a known one must carry its +// declared type. // @ts-expect-error sample.children[0].missing; >sample.children[0].missing : error diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.errors.txt b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.errors.txt index 66f5054ded636..548860ad68a04 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.errors.txt @@ -1,20 +1,18 @@ -recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts(20,16): error TS2339: Property 'run' does not exist on type 'string'. +recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts(18,16): error TS2339: Property 'run' does not exist on type 'string'. ==== recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts (1 errors) ==== // The companion to recursiveTypeThroughObjectLiteralGetterAbsentProperty, on the speculative path. - // Inferring `T` from `Wrapper` opens a region, and checking the inferred type against the - // constraint reads `D` while its base is still being instantiated. A conditional asked about `D` in - // that window used to be told every property matched, so `[D] extends [Need]` took the true branch - // and the index signature became `{ run(): void }` -- `d.anything.run()` then went unreported. + // Inferring `T` from `Wrapper` opens a region, and checking the inferred type against the constraint + // reads `D` while its base is still being instantiated. A conditional asked about `D` in that window used + // to be told every property matched, so `[D] extends [Need]` took the true branch, the index signature + // became `{ run(): void }`, and `d.anything.run()` went unreported. // - // The window withholds inherited members and nothing else, so `missing` -- which no base of `D` - // declares -- is genuinely absent even there. The conditional has to take the same branch it would - // take once the table is complete, and the call has to stay an error. + // The window withholds inherited members and nothing else, so `missing`, which no base of `D` declares, + // is absent even there. The conditional takes the branch it would take once the table is complete. // - // The opposite direction -- a name a base does declare, which has to stay withheld -- is guarded by - // the two hover tests for #62181 and by the mapped-type case, all three of which fail if the miss is - // reported instead. A case written here for it would not reach the skip at all. + // The opposite direction, a name a base does declare and which stays withheld, is covered by the two + // #62181 hover tests and the mapped-type case; a case written here would not reach the skip. export function run(): void { force(w); diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.symbols index 8f58f07ce533b..3efeebcca17b3 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.symbols +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.symbols @@ -2,82 +2,80 @@ === recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts === // The companion to recursiveTypeThroughObjectLiteralGetterAbsentProperty, on the speculative path. -// Inferring `T` from `Wrapper` opens a region, and checking the inferred type against the -// constraint reads `D` while its base is still being instantiated. A conditional asked about `D` in -// that window used to be told every property matched, so `[D] extends [Need]` took the true branch -// and the index signature became `{ run(): void }` -- `d.anything.run()` then went unreported. +// Inferring `T` from `Wrapper` opens a region, and checking the inferred type against the constraint +// reads `D` while its base is still being instantiated. A conditional asked about `D` in that window used +// to be told every property matched, so `[D] extends [Need]` took the true branch, the index signature +// became `{ run(): void }`, and `d.anything.run()` went unreported. // -// The window withholds inherited members and nothing else, so `missing` -- which no base of `D` -// declares -- is genuinely absent even there. The conditional has to take the same branch it would -// take once the table is complete, and the call has to stay an error. +// The window withholds inherited members and nothing else, so `missing`, which no base of `D` declares, +// is absent even there. The conditional takes the branch it would take once the table is complete. // -// The opposite direction -- a name a base does declare, which has to stay withheld -- is guarded by -// the two hover tests for #62181 and by the mapped-type case, all three of which fail if the miss is -// reported instead. A case written here for it would not reach the skip at all. +// The opposite direction, a name a base does declare and which stays withheld, is covered by the two +// #62181 hover tests and the mapped-type case; a case written here would not reach the skip. export function run(): void { >run : Symbol(run, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 0, 0)) force(w); ->force : Symbol(force, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 26, 38)) ->w : Symbol(w, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 28, 13)) +>force : Symbol(force, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 38)) +>w : Symbol(w, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 26, 13)) } export function probe(): void { ->probe : Symbol(probe, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 16, 1)) +>probe : Symbol(probe, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 14, 1)) d.anything.run(); ->d.anything : Symbol(D.__index, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 19)) ->d : Symbol(d, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 29, 13)) ->anything : Symbol(D.__index, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 19)) +>d.anything : Symbol(D.__index, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 19)) +>d : Symbol(d, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 27, 13)) +>anything : Symbol(D.__index, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 19)) } interface Need { missing: string } ->Need : Symbol(Need, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 20, 1)) ->missing : Symbol(Need.missing, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 16)) +>Need : Symbol(Need, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 18, 1)) +>missing : Symbol(Need.missing, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 20, 16)) type Guarded = [T] extends [Need] ? { run(): void } : string; ->Guarded : Symbol(Guarded, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 34)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 13)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 13)) ->Need : Symbol(Need, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 20, 1)) ->run : Symbol(run, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 40)) +>Guarded : Symbol(Guarded, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 20, 34)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 21, 13)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 21, 13)) +>Need : Symbol(Need, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 18, 1)) +>run : Symbol(run, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 21, 40)) interface Base { [k: string]: Guarded; } ->Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 64)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 15)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 21)) ->Guarded : Symbol(Guarded, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 34)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 15)) +>Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 21, 64)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 15)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 21)) +>Guarded : Symbol(Guarded, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 20, 34)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 15)) interface D extends Base { own: never; } ->D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 46)) ->Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 64)) ->D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 46)) ->own : Symbol(D.own, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 25, 29)) +>D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 46)) +>Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 21, 64)) +>D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 46)) +>own : Symbol(D.own, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 29)) interface Wrapper { readonly w: T } ->Wrapper : Symbol(Wrapper, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 25, 43)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 26, 18)) ->w : Symbol(Wrapper.w, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 26, 22)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 26, 18)) +>Wrapper : Symbol(Wrapper, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 43)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 18)) +>w : Symbol(Wrapper.w, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 22)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 18)) declare function force(x: Wrapper): T; ->force : Symbol(force, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 26, 38)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 27, 23)) ->own : Symbol(own, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 27, 34)) ->x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 27, 49)) ->Wrapper : Symbol(Wrapper, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 25, 43)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 27, 23)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 27, 23)) +>force : Symbol(force, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 38)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 25, 23)) +>own : Symbol(own, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 25, 34)) +>x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 25, 49)) +>Wrapper : Symbol(Wrapper, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 43)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 25, 23)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 25, 23)) declare const w: Wrapper; ->w : Symbol(w, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 28, 13)) ->Wrapper : Symbol(Wrapper, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 25, 43)) ->D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 46)) +>w : Symbol(w, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 26, 13)) +>Wrapper : Symbol(Wrapper, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 23, 43)) +>D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 46)) declare const d: D; ->d : Symbol(d, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 29, 13)) ->D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 24, 46)) +>d : Symbol(d, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 27, 13)) +>D : Symbol(D, Decl(recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts, 22, 46)) diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.types index 0d5d70d732f9d..e00ab7d6ed002 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.types +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.types @@ -2,18 +2,16 @@ === recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts === // The companion to recursiveTypeThroughObjectLiteralGetterAbsentProperty, on the speculative path. -// Inferring `T` from `Wrapper` opens a region, and checking the inferred type against the -// constraint reads `D` while its base is still being instantiated. A conditional asked about `D` in -// that window used to be told every property matched, so `[D] extends [Need]` took the true branch -// and the index signature became `{ run(): void }` -- `d.anything.run()` then went unreported. +// Inferring `T` from `Wrapper` opens a region, and checking the inferred type against the constraint +// reads `D` while its base is still being instantiated. A conditional asked about `D` in that window used +// to be told every property matched, so `[D] extends [Need]` took the true branch, the index signature +// became `{ run(): void }`, and `d.anything.run()` went unreported. // -// The window withholds inherited members and nothing else, so `missing` -- which no base of `D` -// declares -- is genuinely absent even there. The conditional has to take the same branch it would -// take once the table is complete, and the call has to stay an error. +// The window withholds inherited members and nothing else, so `missing`, which no base of `D` declares, +// is absent even there. The conditional takes the branch it would take once the table is complete. // -// The opposite direction -- a name a base does declare, which has to stay withheld -- is guarded by -// the two hover tests for #62181 and by the mapped-type case, all three of which fail if the miss is -// reported instead. A case written here for it would not reach the skip at all. +// The opposite direction, a name a base does declare and which stays withheld, is covered by the two +// #62181 hover tests and the mapped-type case; a case written here would not reach the skip. export function run(): void { >run : () => void diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.errors.txt b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.errors.txt index 3c3405cb2fa92..edc42211254c9 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.errors.txt @@ -4,8 +4,8 @@ recursiveTypeThroughObjectLiteralGetterConstraint.ts(15,9): error TS2741: Proper ==== recursiveTypeThroughObjectLiteralGetterConstraint.ts (1 errors) ==== // Skipping an accessor during a check that reports nothing is a deferral, not a waiver. An array of // schemas is not a Schema, so this reports. The baseline is byte-identical to what an unpatched - // compiler produces, and that is the whole point of the case: it pins that the constraint is still - // enforced, not which pass enforced it. The recursive shape where the deferred pass is the only thing + // compiler produces, pinning that the constraint is still enforced rather than which pass enforced + // it. The recursive shape where the deferred pass is the only thing // that can report it is recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts. interface Schema { diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.symbols index 9de6d7b8a1713..123e6ce1bcdce 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.symbols +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.symbols @@ -3,8 +3,8 @@ === recursiveTypeThroughObjectLiteralGetterConstraint.ts === // Skipping an accessor during a check that reports nothing is a deferral, not a waiver. An array of // schemas is not a Schema, so this reports. The baseline is byte-identical to what an unpatched -// compiler produces, and that is the whole point of the case: it pins that the constraint is still -// enforced, not which pass enforced it. The recursive shape where the deferred pass is the only thing +// compiler produces, pinning that the constraint is still enforced rather than which pass enforced +// it. The recursive shape where the deferred pass is the only thing // that can report it is recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts. interface Schema { diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.types index e3e78e9315ac5..58c76eb798070 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.types +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.types @@ -3,8 +3,8 @@ === recursiveTypeThroughObjectLiteralGetterConstraint.ts === // Skipping an accessor during a check that reports nothing is a deferral, not a waiver. An array of // schemas is not a Schema, so this reports. The baseline is byte-identical to what an unpatched -// compiler produces, and that is the whole point of the case: it pins that the constraint is still -// enforced, not which pass enforced it. The recursive shape where the deferred pass is the only thing +// compiler produces, pinning that the constraint is still enforced rather than which pass enforced +// it. The recursive shape where the deferred pass is the only thing // that can report it is recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts. interface Schema { diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.symbols index f01a3fabe2b13..f9d459f7d337b 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.symbols +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.symbols @@ -3,180 +3,177 @@ === recursiveTypeThroughObjectLiteralGetterFlowLoop.ts === // A recursive getter whose body runs a loop, assigning to a union-typed variable. // -// The loop puts entries on the flow-loop stack, and the assignment's declared type being a union is -// what makes control flow analysis evaluate the assigned expression rather than take the declared -// type. So the inner getter is forced from inside a loop back-edge, while a provisional comparison is -// open. +// The loop puts entries on the flow-loop stack, and the union declared type makes control flow analysis +// evaluate the assigned expression rather than take the declared type, so the inner getter is forced +// from inside a loop back-edge while a provisional comparison is open. // -// This is the case that separates saving a stack's length from saving the stack itself. -// `checkExpressionCachedEx` swaps in a nil flow-loop stack and puts the original back only when it -// returns normally, so an unwind that crosses it leaves the nil in place. Restoring by length then -// re-slices the replacement instead of the saved stack, which is a runtime panic rather than a -// diagnostic. The three stacks some caller replaces wholesale are therefore held as slice headers. +// checkExpressionCachedEx swaps in a nil flow-loop stack and restores it only on a normal return, so an +// unwind crossing it leaves the nil in place. Restoring by length then re-slices the replacement rather +// than the saved stack, which is a panic rather than a diagnostic. The three stacks a caller replaces +// wholesale are held as slice headers for this reason. interface Internals { >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 0, 0)) ->O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 20)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 11, 20)) readonly out: O; ->out : Symbol(Internals.out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 38)) ->O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 20)) +>out : Symbol(Internals.out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 11, 38)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 11, 20)) } interface Schema { ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 1)) readonly internals: Internals; ->internals : Symbol(Schema.internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 17, 18)) +>internals : Symbol(Schema.internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 18)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 0, 0)) } type Out = T["internals"]["out"]; ->Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 19, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 21, 9)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 21, 9)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 17, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 19, 9)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 19, 9)) interface ArrayInternals extends Internals[]> {} ->ArrayInternals : Symbol(ArrayInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 21, 51)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 23, 25)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) +>ArrayInternals : Symbol(ArrayInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 19, 51)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 21, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 1)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 0, 0)) ->Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 19, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 23, 25)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 17, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 21, 25)) interface ArraySchema extends Schema { ->ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 23, 73)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 24, 22)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) +>ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 21, 73)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 22, 22)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 1)) readonly internals: ArrayInternals; ->internals : Symbol(ArraySchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 24, 65)) ->ArrayInternals : Symbol(ArrayInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 21, 51)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 24, 22)) +>internals : Symbol(ArraySchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 22, 65)) +>ArrayInternals : Symbol(ArrayInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 19, 51)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 22, 22)) } interface StringInternals extends Internals {} ->StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 26, 1)) +>StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 24, 1)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 0, 0)) interface StringSchema extends Schema { ->StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 28, 54)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 26, 54)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 1)) readonly internals: StringInternals; ->internals : Symbol(StringSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 29, 39)) ->StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 26, 1)) +>internals : Symbol(StringSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 27, 39)) +>StringInternals : Symbol(StringInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 24, 1)) } type Shape = Readonly<{ [k: string]: Schema }>; ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 31, 1)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 29, 1)) >Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 33, 25)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 31, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 1)) interface ObjectInternals extends Internals<{ [K in keyof S]: Out }> {} ->ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 33, 47)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 26)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 31, 1)) +>ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 31, 47)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 32, 26)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 29, 1)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 0, 0)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 64)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 26)) ->Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 19, 1)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 26)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 64)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 32, 64)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 32, 26)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 17, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 32, 26)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 32, 64)) interface ObjectSchema extends Schema { ->ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 94)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 35, 23)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 31, 1)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 31, 1)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 32, 94)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 33, 23)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 29, 1)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 29, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 1)) readonly internals: ObjectInternals; ->internals : Symbol(ObjectSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 35, 64)) ->ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 33, 47)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 35, 23)) +>internals : Symbol(ObjectSchema.internals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 33, 64)) +>ObjectInternals : Symbol(ObjectInternals, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 31, 47)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 33, 23)) } declare function object(shape: S): ObjectSchema; ->object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 37, 1)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 39, 24)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 31, 1)) ->shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 39, 41)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 39, 24)) ->ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 34, 94)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 39, 24)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 35, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 37, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 29, 1)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 37, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 37, 24)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 32, 94)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 37, 24)) declare function array(element: T): ArraySchema; ->array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 39, 68)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 40, 23)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) ->element : Symbol(element, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 40, 41)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 40, 23)) ->ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 23, 73)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 40, 23)) +>array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 37, 68)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 38, 23)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 1)) +>element : Symbol(element, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 38, 41)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 38, 23)) +>ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 21, 73)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 38, 23)) declare function text(): StringSchema; ->text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 40, 69)) ->StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 28, 54)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 38, 69)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 26, 54)) const node = object({ ->node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 43, 5)) ->object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 37, 1)) +>node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 41, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 35, 1)) name: text(), ->name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 43, 21)) ->text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 40, 69)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 41, 21)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 38, 69)) get children() { ->children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 44, 17)) +>children : Symbol(children, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 42, 17)) let holder: Schema | null = null; ->holder : Symbol(holder, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 46, 11)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 15, 1)) +>holder : Symbol(holder, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 44, 11)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 13, 1)) for (let i = 0; i < 2; i++) { ->i : Symbol(i, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 47, 16)) ->i : Symbol(i, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 47, 16)) ->i : Symbol(i, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 47, 16)) +>i : Symbol(i, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 45, 16)) +>i : Symbol(i, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 45, 16)) +>i : Symbol(i, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 45, 16)) holder = object({ ->holder : Symbol(holder, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 46, 11)) ->object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 37, 1)) +>holder : Symbol(holder, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 44, 11)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 35, 1)) get inner() { ->inner : Symbol(inner, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 48, 29)) +>inner : Symbol(inner, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 46, 29)) return array(node); ->array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 39, 68)) ->node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 43, 5)) +>array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 37, 68)) +>node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 41, 5)) }, }); } return holder; ->holder : Symbol(holder, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 46, 11)) +>holder : Symbol(holder, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 44, 11)) }, }); declare const sample: Out; ->sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 58, 13)) ->Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 19, 1)) ->node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 43, 5)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 56, 13)) +>Out : Symbol(Out, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 17, 1)) +>node : Symbol(node, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 41, 5)) const topName: string = sample.name; ->topName : Symbol(topName, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 59, 5)) ->sample.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 43, 21)) ->sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 58, 13)) ->name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 43, 21)) +>topName : Symbol(topName, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 57, 5)) +>sample.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 41, 21)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 56, 13)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 41, 21)) -// A key the shape does not declare is still absent, which is what tells a resolved type apart from -// one that collapsed to `any`. +// A key the shape does not declare is still absent; a collapse to `any` would permit this. // @ts-expect-error sample.missing; ->sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 58, 13)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterFlowLoop.ts, 56, 13)) diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.types index 931a0adb097bf..f36acccbd1fd9 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.types +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.types @@ -3,16 +3,14 @@ === recursiveTypeThroughObjectLiteralGetterFlowLoop.ts === // A recursive getter whose body runs a loop, assigning to a union-typed variable. // -// The loop puts entries on the flow-loop stack, and the assignment's declared type being a union is -// what makes control flow analysis evaluate the assigned expression rather than take the declared -// type. So the inner getter is forced from inside a loop back-edge, while a provisional comparison is -// open. +// The loop puts entries on the flow-loop stack, and the union declared type makes control flow analysis +// evaluate the assigned expression rather than take the declared type, so the inner getter is forced +// from inside a loop back-edge while a provisional comparison is open. // -// This is the case that separates saving a stack's length from saving the stack itself. -// `checkExpressionCachedEx` swaps in a nil flow-loop stack and puts the original back only when it -// returns normally, so an unwind that crosses it leaves the nil in place. Restoring by length then -// re-slices the replacement instead of the saved stack, which is a runtime panic rather than a -// diagnostic. The three stacks some caller replaces wholesale are therefore held as slice headers. +// checkExpressionCachedEx swaps in a nil flow-loop stack and restores it only on a normal return, so an +// unwind crossing it leaves the nil in place. Restoring by length then re-slices the replacement rather +// than the saved stack, which is a panic rather than a diagnostic. The three stacks a caller replaces +// wholesale are held as slice headers for this reason. interface Internals { readonly out: O; @@ -120,8 +118,7 @@ const topName: string = sample.name; >sample : { name: string; readonly children: unknown; } >name : string -// A key the shape does not declare is still absent, which is what tells a resolved type apart from -// one that collapsed to `any`. +// A key the shape does not declare is still absent; a collapse to `any` would permit this. // @ts-expect-error sample.missing; >sample.missing : error diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols index f28ec5e547b85..127cf5ae283a5 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.symbols @@ -1,228 +1,221 @@ //// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts] //// === recursiveTypeThroughObjectLiteralGetterInheritedMember.ts === -// The shape from #62180, where the recursive member is reached through a mapped type that remaps its -// keys by reading each member's own discriminant. Resolving the object schema's members instantiates -// its base with a type that names the schema back, so the schema's table is read while it is still -// being assembled and its inherited `out` is not in it yet. +// The shape from #62180. The recursive member is reached through a mapped type that remaps keys by +// reading each member's discriminant, so resolving the object schema instantiates its base with a type +// naming the schema back, and the table is read before its inherited `out` is added. // -// A lookup that misses in that window finishes itself against the base types still to be inherited, -// so it returns the member the table is about to hold. An earlier draft instead answered such a miss -// with `any`, which cleared the error here and was wrong everywhere else: a self-referential -// interface reaches the same window with no getter and no inference anywhere in the program, and an -// unpatched compiler answers it from the index signature, so `any` silently accepted an indexed -// access that every other compiler rejects. Completing the lookup is what separates the two -- a name -// no base carries is still absent. +// A miss there resolves against the bases still to be inherited. Answering `any` instead would clear +// this error and break the self-referential interface case, where an unpatched compiler answers from the +// index signature and `any` would accept an indexed access every other compiler rejects. // -// The `any` that appears against `parent` in the .types baseline is the printer eliding a recursive -// reference, not an unresolved member. The assertions below are what distinguish those two. +// The `any` against `parent` in the .types baseline is printer elision, not an unresolved member; the +// assertions below tell those apart. interface Internals { >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 0, 0)) ->O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 16, 20)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 11, 20)) optional: "true" | "false"; ->optional : Symbol(Internals.optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 16, 24)) +>optional : Symbol(Internals.optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 11, 24)) out: O; ->out : Symbol(Internals.out, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 17, 31)) ->O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 16, 20)) +>out : Symbol(Internals.out, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 12, 31)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 11, 20)) } interface StringSchema extends Internals { ->StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 19, 1)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 14, 1)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 0, 0)) optional: "false"; ->optional : Symbol(StringSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 21, 50)) +>optional : Symbol(StringSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 16, 50)) } type Shape = Record; ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 23, 1)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 18, 1)) >Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) type Prettify = { [K in keyof T]: T[K] } & {}; ->Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 25, 33)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 14)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 22)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 14)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 14)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 22)) +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 20, 33)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 21, 14)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 21, 22)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 21, 14)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 21, 14)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 21, 22)) type ObjectOut = Prettify< ->ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 49)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 23, 1)) ->Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 25, 33)) +>ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 21, 49)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 22, 15)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 18, 1)) +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 20, 33)) { [K in keyof S as S[K] extends { optional: "true" } ? K : never]?: S[K]["out"]; ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 29, 9)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 29, 9)) ->optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 29, 39)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 29, 9)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 29, 9)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 24, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 22, 15)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 22, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 24, 9)) +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 24, 39)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 24, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 22, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 24, 9)) } & { [K in keyof S as S[K] extends { optional: "true" } ? never : K]: S[K]["out"]; ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 31, 9)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 31, 9)) ->optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 31, 39)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 31, 9)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 27, 15)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 31, 9)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 22, 15)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 22, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 9)) +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 39)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 22, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 9)) } >; interface ObjectSchema extends Internals> { ->ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 33, 2)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 35, 23)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 23, 1)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 28, 2)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 30, 23)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 18, 1)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 0, 0)) ->ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 26, 49)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 35, 23)) +>ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 21, 49)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 30, 23)) optional: "false"; ->optional : Symbol(ObjectSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 35, 73)) +>optional : Symbol(ObjectSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 30, 73)) } interface OptionalSchema> extends Internals { ->OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 37, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 39, 25)) +>OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 32, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 34, 25)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 0, 0)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 0, 0)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 39, 25)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 34, 25)) optional: "true"; ->optional : Symbol(OptionalSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 39, 92)) +>optional : Symbol(OptionalSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 34, 92)) } declare function object(shape: S): ObjectSchema; ->object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 41, 1)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 24)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 23, 1)) ->shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 41)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 24)) ->ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 33, 2)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 24)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 36, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 38, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 18, 1)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 38, 41)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 38, 24)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 28, 2)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 38, 24)) declare function text(): StringSchema; ->text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 68)) ->StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 19, 1)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 38, 68)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 14, 1)) declare function optional>(schema: T): OptionalSchema; ->optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 44, 38)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 45, 26)) +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 39, 38)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 40, 26)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 0, 0)) ->schema : Symbol(schema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 45, 52)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 45, 26)) ->OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 37, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 45, 26)) +>schema : Symbol(schema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 40, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 40, 26)) +>OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 32, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 40, 26)) const category = object({ ->category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 5)) ->object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 41, 1)) +>category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 42, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 36, 1)) name: text(), ->name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) ->text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 68)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 42, 25)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 38, 68)) get parent() { ->parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) return optional(category); ->optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 44, 38)) ->category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 5)) +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 39, 38)) +>category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 42, 5)) }, }); declare const sample: (typeof category)["out"]; ->sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) ->category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 5)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 49, 13)) +>category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 42, 5)) -// The key that does not recurse resolves fully, and the remapping put the recursive one in the -// optional bucket, so it is `parent?:` rather than `parent:`. Both of those are new: main gives the -// whole declaration an implicit `any` and reports it. +// The non-recursive key resolves fully, and the remapping put the recursive one in the optional bucket, +// so it is `parent?:`. Both are new; main gives the whole declaration an implicit `any`. const topName: string = sample.name; ->topName : Symbol(topName, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 58, 5)) ->sample.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) ->sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) ->name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) +>topName : Symbol(topName, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 52, 5)) +>sample.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 42, 25)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 49, 13)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 42, 25)) -// The recursive one resolves too, and keeps resolving as far down as it is walked. +// The recursive one resolves too, at any depth walked. const parentName: string = sample.parent!.name; ->parentName : Symbol(parentName, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 60, 5)) ->sample.parent!.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) ->sample.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) ->parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) +>parentName : Symbol(parentName, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 5)) +>sample.parent!.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 42, 25)) +>sample.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 49, 13)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 42, 25)) const deepName: string = sample.parent!.parent!.parent!.name; ->deepName : Symbol(deepName, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 61, 5)) ->sample.parent!.parent!.parent!.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) ->sample.parent!.parent!.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->sample.parent!.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->sample.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) ->parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) - -// Depth is where a collapse would hide: `any` would accept both of these, so they are what keeps the -// comment above honest. +>deepName : Symbol(deepName, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 55, 5)) +>sample.parent!.parent!.parent!.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 42, 25)) +>sample.parent!.parent!.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>sample.parent!.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>sample.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 49, 13)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 42, 25)) + +// A collapse to `any` would accept both of these. // @ts-expect-error const wrongType: number = sample.parent!.parent!.name; ->wrongType : Symbol(wrongType, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 5)) ->sample.parent!.parent!.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) ->sample.parent!.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->sample.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) ->parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 47, 25)) +>wrongType : Symbol(wrongType, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 58, 5)) +>sample.parent!.parent!.name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 42, 25)) +>sample.parent!.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>sample.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 49, 13)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 42, 25)) // @ts-expect-error sample.parent!.parent!.missing; ->sample.parent!.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->sample.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 54, 13)) ->parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) ->parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 48, 17)) +>sample.parent!.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>sample.parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 49, 13)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 43, 17)) -// A property that is genuinely absent must still be reported, whoever is asking. +// A genuinely absent property is still reported. interface Base { readonly own: number; } ->Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 67, 31)) ->own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 70, 16)) +>Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 60, 31)) +>own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 16)) interface Derived extends Base { readonly extra: string; } ->Derived : Symbol(Derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 70, 40)) ->Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 67, 31)) ->extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 71, 32)) +>Derived : Symbol(Derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 40)) +>Base : Symbol(Base, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 60, 31)) +>extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 64, 32)) declare const derived: Derived; ->derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 72, 13)) ->Derived : Symbol(Derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 70, 40)) +>derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 13)) +>Derived : Symbol(Derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 40)) // @ts-expect-error derived.missing; ->derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 72, 13)) +>derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 13)) const ownValue: number = derived.own; ->ownValue : Symbol(ownValue, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 75, 5)) ->derived.own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 70, 16)) ->derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 72, 13)) ->own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 70, 16)) +>ownValue : Symbol(ownValue, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 68, 5)) +>derived.own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 16)) +>derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 13)) +>own : Symbol(Base.own, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 63, 16)) const extraValue: string = derived.extra; ->extraValue : Symbol(extraValue, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 76, 5)) ->derived.extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 71, 32)) ->derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 72, 13)) ->extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 71, 32)) +>extraValue : Symbol(extraValue, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 69, 5)) +>derived.extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 64, 32)) +>derived : Symbol(derived, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 65, 13)) +>extra : Symbol(Derived.extra, Decl(recursiveTypeThroughObjectLiteralGetterInheritedMember.ts, 64, 32)) diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types index 57b18960ce823..d82eacf99a9bf 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.types @@ -1,21 +1,16 @@ //// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts] //// === recursiveTypeThroughObjectLiteralGetterInheritedMember.ts === -// The shape from #62180, where the recursive member is reached through a mapped type that remaps its -// keys by reading each member's own discriminant. Resolving the object schema's members instantiates -// its base with a type that names the schema back, so the schema's table is read while it is still -// being assembled and its inherited `out` is not in it yet. +// The shape from #62180. The recursive member is reached through a mapped type that remaps keys by +// reading each member's discriminant, so resolving the object schema instantiates its base with a type +// naming the schema back, and the table is read before its inherited `out` is added. // -// A lookup that misses in that window finishes itself against the base types still to be inherited, -// so it returns the member the table is about to hold. An earlier draft instead answered such a miss -// with `any`, which cleared the error here and was wrong everywhere else: a self-referential -// interface reaches the same window with no getter and no inference anywhere in the program, and an -// unpatched compiler answers it from the index signature, so `any` silently accepted an indexed -// access that every other compiler rejects. Completing the lookup is what separates the two -- a name -// no base carries is still absent. +// A miss there resolves against the bases still to be inherited. Answering `any` instead would clear +// this error and break the self-referential interface case, where an unpatched compiler answers from the +// index signature and `any` would accept an indexed access every other compiler rejects. // -// The `any` that appears against `parent` in the .types baseline is the printer eliding a recursive -// reference, not an unresolved member. The assertions below are what distinguish those two. +// The `any` against `parent` in the .types baseline is printer elision, not an unresolved member; the +// assertions below tell those apart. interface Internals { optional: "true" | "false"; @@ -95,16 +90,15 @@ declare const sample: (typeof category)["out"]; >sample : { name: string; readonly parent?: { name: string; readonly parent?: any | undefined; } | undefined; } >category : ObjectSchema<{ name: StringSchema; readonly parent: OptionalSchema>; }> -// The key that does not recurse resolves fully, and the remapping put the recursive one in the -// optional bucket, so it is `parent?:` rather than `parent:`. Both of those are new: main gives the -// whole declaration an implicit `any` and reports it. +// The non-recursive key resolves fully, and the remapping put the recursive one in the optional bucket, +// so it is `parent?:`. Both are new; main gives the whole declaration an implicit `any`. const topName: string = sample.name; >topName : string >sample.name : string >sample : { name: string; readonly parent?: { name: string; readonly parent?: any | undefined; } | undefined; } >name : string -// The recursive one resolves too, and keeps resolving as far down as it is walked. +// The recursive one resolves too, at any depth walked. const parentName: string = sample.parent!.name; >parentName : string >sample.parent!.name : string @@ -129,8 +123,7 @@ const deepName: string = sample.parent!.parent!.parent!.name; >parent : { name: string; readonly parent?: any | undefined; } | undefined >name : string -// Depth is where a collapse would hide: `any` would accept both of these, so they are what keeps the -// comment above honest. +// A collapse to `any` would accept both of these. // @ts-expect-error const wrongType: number = sample.parent!.parent!.name; >wrongType : number @@ -156,7 +149,7 @@ sample.parent!.parent!.missing; >parent : { name: string; readonly parent?: any | undefined; } | undefined >missing : any -// A property that is genuinely absent must still be reported, whoever is asking. +// A genuinely absent property is still reported. interface Base { readonly own: number; } >own : number diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.symbols index 73119b911f195..6c63830027b81 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.symbols +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.symbols @@ -1,275 +1,272 @@ //// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterMutual.ts] //// === recursiveTypeThroughObjectLiteralGetterMutual.ts === -// Two schemas that name each other through getters, with the object's members reached through two -// separate key remappings -- one for the output side and one for the input side, each keyed on a -// different member of the schema's own internals. +// Two schemas naming each other through getters, with the object's members reached through two separate +// key remappings, one per variance side, each keyed on a different member of the internals. // -// Both remappings read the member table of a type whose bases are still being inherited, and they -// read it at different points in that assembly. That is what makes this shape the one that separates -// the two answers a miss in that window can have. A speculative comparison has to decline, because -// the member it would force is the one its own question is about. An ordinary lookup has to finish -// itself against the base types still to be inherited, which is what keeps the window unobservable. -// Giving a speculative miss the second answer forces `posts` while its own return type is still -// being inferred, and the getter falls back to an implicit `any` -- so the two cannot be merged into -// one answer, and a single remapping does not reach the case. +// Both remappings read the member table of a type whose bases are still being inherited, at different +// points in that assembly, which is what makes this shape separate the two answers a miss can have. A +// speculative comparison must decline, since the member it would force is the subject of its own +// question. An ordinary lookup must resolve against the bases still to be inherited. Giving a speculative +// miss the second answer forces `posts` mid-inference and the getter falls back to an implicit `any`. A +// single remapping does not reach this. interface Internals { >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 0, 0)) ->O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 13, 20)) ->I : Symbol(I, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 13, 36)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 10, 20)) +>I : Symbol(I, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 10, 36)) output: O; ->output : Symbol(Internals.output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 13, 55)) ->O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 13, 20)) +>output : Symbol(Internals.output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 10, 55)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 10, 20)) input: I; ->input : Symbol(Internals.input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 14, 14)) ->I : Symbol(I, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 13, 36)) +>input : Symbol(Internals.input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 11, 14)) +>I : Symbol(I, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 10, 36)) optin?: "optional" | undefined; ->optin : Symbol(Internals.optin, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 15, 13)) +>optin : Symbol(Internals.optin, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 12, 13)) optout?: "optional" | undefined; ->optout : Symbol(Internals.optout, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 16, 35)) +>optout : Symbol(Internals.optout, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 13, 35)) } interface Schema { ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) ->O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 20, 17)) ->I : Symbol(I, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 20, 33)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 15, 1)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 17, 17)) +>I : Symbol(I, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 17, 33)) _zod: Internals; ->_zod : Symbol(Schema._zod, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 20, 52)) +>_zod : Symbol(Schema._zod, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 17, 52)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 0, 0)) ->O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 20, 17)) ->I : Symbol(I, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 20, 33)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 17, 17)) +>I : Symbol(I, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 17, 33)) } type output = T["_zod"]["output"]; ->output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 12)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 12)) +>output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 19, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 21, 12)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 15, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 21, 12)) type input = T["_zod"]["input"]; ->input : Symbol(input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 52)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 25, 11)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 25, 11)) +>input : Symbol(input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 21, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 11)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 15, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 11)) interface StringSchema extends Schema {} ->StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 25, 50)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 50)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 15, 1)) declare function text(): StringSchema; ->text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 27, 56)) ->StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 25, 50)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 56)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 50)) interface ArraySchema extends Schema[], input[]> {} ->ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 28, 38)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 22)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) ->output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 22)) ->input : Symbol(input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 52)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 22)) +>ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 25, 38)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 27, 22)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 15, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 15, 1)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 15, 1)) +>output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 19, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 27, 22)) +>input : Symbol(input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 21, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 27, 22)) declare function array(element: T): ArraySchema; ->array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 91)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 23)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) ->element : Symbol(element, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 41)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 23)) ->ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 28, 38)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 23)) +>array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 27, 91)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 28, 23)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 15, 1)) +>element : Symbol(element, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 28, 41)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 28, 23)) +>ArraySchema : Symbol(ArraySchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 25, 38)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 28, 23)) type OptionalOutSchema = { _zod: { optout: "optional" } }; ->OptionalOutSchema : Symbol(OptionalOutSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 69)) ->_zod : Symbol(_zod, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 26)) ->optout : Symbol(optout, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 34)) +>OptionalOutSchema : Symbol(OptionalOutSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 28, 69)) +>_zod : Symbol(_zod, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 26)) +>optout : Symbol(optout, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 34)) type OptionalInSchema = { _zod: { optin: "optional" } }; ->OptionalInSchema : Symbol(OptionalInSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 58)) ->_zod : Symbol(_zod, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 25)) ->optin : Symbol(optin, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 33)) +>OptionalInSchema : Symbol(OptionalInSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 58)) +>_zod : Symbol(_zod, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 25)) +>optin : Symbol(optin, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 33)) type Shape = Readonly<{ [k: string]: Schema }>; ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 56)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 56)) >Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 25)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 32, 25)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 15, 1)) type Prettify = { [K in keyof T]: T[K] } & {}; ->Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 47)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 14)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 22)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 14)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 14)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 22)) +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 32, 47)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 14)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 22)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 14)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 14)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 22)) type ObjectOut = Prettify< ->ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 49)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 56)) ->Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 47)) +>ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 49)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 15)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 56)) +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 32, 47)) { [k in keyof T as T[k] extends OptionalOutSchema ? never : k]: output; ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 40, 9)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 40, 9)) ->OptionalOutSchema : Symbol(OptionalOutSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 69)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 40, 9)) ->output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 40, 9)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 37, 9)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 15)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 15)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 37, 9)) +>OptionalOutSchema : Symbol(OptionalOutSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 28, 69)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 37, 9)) +>output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 19, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 15)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 37, 9)) } & { [k in keyof T as T[k] extends OptionalOutSchema ? k : never]?: output; ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 42, 9)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 42, 9)) ->OptionalOutSchema : Symbol(OptionalOutSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 69)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 42, 9)) ->output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 38, 15)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 42, 9)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 39, 9)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 15)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 15)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 39, 9)) +>OptionalOutSchema : Symbol(OptionalOutSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 28, 69)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 39, 9)) +>output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 19, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 15)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 39, 9)) } >; type ObjectIn = Prettify< ->ObjectIn : Symbol(ObjectIn, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 44, 2)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 56)) ->Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 35, 47)) +>ObjectIn : Symbol(ObjectIn, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 41, 2)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 43, 14)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 56)) +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 32, 47)) { [k in keyof T as T[k] extends OptionalInSchema ? never : k]: input; ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 48, 9)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 48, 9)) ->OptionalInSchema : Symbol(OptionalInSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 58)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 48, 9)) ->input : Symbol(input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 52)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 48, 9)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 45, 9)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 43, 14)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 43, 14)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 45, 9)) +>OptionalInSchema : Symbol(OptionalInSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 58)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 45, 9)) +>input : Symbol(input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 21, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 43, 14)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 45, 9)) } & { [k in keyof T as T[k] extends OptionalInSchema ? k : never]?: input; ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 50, 9)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 50, 9)) ->OptionalInSchema : Symbol(OptionalInSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 58)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 50, 9)) ->input : Symbol(input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 52)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 46, 14)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 50, 9)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 47, 9)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 43, 14)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 43, 14)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 47, 9)) +>OptionalInSchema : Symbol(OptionalInSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 58)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 47, 9)) +>input : Symbol(input, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 21, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 43, 14)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 47, 9)) } >; interface ObjectSchema extends Schema, ObjectIn> { ->ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 52, 2)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 23)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 56)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 56)) ->Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 18, 1)) ->ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 36, 49)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 23)) ->ObjectIn : Symbol(ObjectIn, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 44, 2)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 23)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 49, 2)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 51, 23)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 56)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 56)) +>Schema : Symbol(Schema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 15, 1)) +>ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 33, 49)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 51, 23)) +>ObjectIn : Symbol(ObjectIn, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 41, 2)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 51, 23)) shape: S; ->shape : Symbol(ObjectSchema.shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 91)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 23)) +>shape : Symbol(ObjectSchema.shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 51, 91)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 51, 23)) } declare function object(shape: T): ObjectSchema; ->object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 57, 24)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 34, 56)) ->shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 57, 41)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 57, 24)) ->ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 52, 2)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 57, 24)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 53, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 31, 56)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 41)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 24)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 49, 2)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 54, 24)) const user = object({ ->user : Symbol(user, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 5)) ->object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 1)) +>user : Symbol(user, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 53, 1)) email: text(), ->email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 21)) ->text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 27, 56)) +>email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 21)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 56)) get posts() { ->posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 60, 18)) +>posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 57, 18)) return array(post); ->array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 30, 91)) ->post : Symbol(post, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 66, 5)) +>array : Symbol(array, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 27, 91)) +>post : Symbol(post, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 63, 5)) }, }); const post = object({ ->post : Symbol(post, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 66, 5)) ->object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 1)) +>post : Symbol(post, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 63, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 53, 1)) title: text(), ->title : Symbol(title, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 66, 21)) ->text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 27, 56)) +>title : Symbol(title, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 63, 21)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 24, 56)) get author() { ->author : Symbol(author, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 67, 18)) +>author : Symbol(author, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 64, 18)) return user; ->user : Symbol(user, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 5)) +>user : Symbol(user, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 5)) }, }); type UserT = output; ->UserT : Symbol(UserT, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 71, 3)) ->output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 22, 1)) ->user : Symbol(user, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 5)) +>UserT : Symbol(UserT, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 68, 3)) +>output : Symbol(output, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 19, 1)) +>user : Symbol(user, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 5)) declare const sampleUser: UserT; ->sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 74, 13)) ->UserT : Symbol(UserT, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 71, 3)) +>sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 71, 13)) +>UserT : Symbol(UserT, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 68, 3)) // Every step of the cycle resolves, including the one that walks back to where it started. On main // both declarations get an implicit `any` instead. const email: string = sampleUser.email; ->email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 77, 5)) ->sampleUser.email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 21)) ->sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 74, 13)) ->email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 21)) +>email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 74, 5)) +>sampleUser.email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 21)) +>sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 71, 13)) +>email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 21)) const postTitle: string = sampleUser.posts[0].title; ->postTitle : Symbol(postTitle, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 78, 5)) ->sampleUser.posts[0].title : Symbol(title, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 66, 21)) ->sampleUser.posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 60, 18)) ->sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 74, 13)) ->posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 60, 18)) ->title : Symbol(title, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 66, 21)) +>postTitle : Symbol(postTitle, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 75, 5)) +>sampleUser.posts[0].title : Symbol(title, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 63, 21)) +>sampleUser.posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 57, 18)) +>sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 71, 13)) +>posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 57, 18)) +>title : Symbol(title, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 63, 21)) const authorEmail: string = sampleUser.posts[0].author.email; ->authorEmail : Symbol(authorEmail, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 79, 5)) ->sampleUser.posts[0].author.email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 21)) ->sampleUser.posts[0].author : Symbol(author, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 67, 18)) ->sampleUser.posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 60, 18)) ->sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 74, 13)) ->posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 60, 18)) ->author : Symbol(author, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 67, 18)) ->email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 59, 21)) +>authorEmail : Symbol(authorEmail, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 76, 5)) +>sampleUser.posts[0].author.email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 21)) +>sampleUser.posts[0].author : Symbol(author, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 64, 18)) +>sampleUser.posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 57, 18)) +>sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 71, 13)) +>posts : Symbol(posts, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 57, 18)) +>author : Symbol(author, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 64, 18)) +>email : Symbol(email, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 56, 21)) // The printer elides a recursive reference as `any`, so a collapsed result prints the same as a // resolved one. Reading a key neither schema declares is what tells them apart: it is an error here // and permitted on `any`, so a collapse turns this directive into an unused one. // @ts-expect-error sampleUser.missing; ->sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 74, 13)) +>sampleUser : Symbol(sampleUser, Decl(recursiveTypeThroughObjectLiteralGetterMutual.ts, 71, 13)) diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.types index 2925d74f40c01..3550df8518c9f 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.types +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterMutual.types @@ -1,18 +1,15 @@ //// [tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterMutual.ts] //// === recursiveTypeThroughObjectLiteralGetterMutual.ts === -// Two schemas that name each other through getters, with the object's members reached through two -// separate key remappings -- one for the output side and one for the input side, each keyed on a -// different member of the schema's own internals. +// Two schemas naming each other through getters, with the object's members reached through two separate +// key remappings, one per variance side, each keyed on a different member of the internals. // -// Both remappings read the member table of a type whose bases are still being inherited, and they -// read it at different points in that assembly. That is what makes this shape the one that separates -// the two answers a miss in that window can have. A speculative comparison has to decline, because -// the member it would force is the one its own question is about. An ordinary lookup has to finish -// itself against the base types still to be inherited, which is what keeps the window unobservable. -// Giving a speculative miss the second answer forces `posts` while its own return type is still -// being inferred, and the getter falls back to an implicit `any` -- so the two cannot be merged into -// one answer, and a single remapping does not reach the case. +// Both remappings read the member table of a type whose bases are still being inherited, at different +// points in that assembly, which is what makes this shape separate the two answers a miss can have. A +// speculative comparison must decline, since the member it would force is the subject of its own +// question. An ordinary lookup must resolve against the bases still to be inherited. Giving a speculative +// miss the second answer forces `posts` mid-inference and the getter falls back to an implicit `any`. A +// single remapping does not reach this. interface Internals { output: O; diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.symbols index f02e1edc5463b..2292c3d08e163 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.symbols +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.symbols @@ -5,100 +5,99 @@ // about the program, so skipping one on that alone makes an ordinary comparison depend on // resolution order. This picked the numeric overload for an object whose only member is a string. // -// The generic pair at the bottom matters more than the plain one: only a generic signature opens the -// speculative region where the skip is allowed at all, so the non-generic overloads never reach the -// code under test. Skipping there made the object look like it had no string member and picked the +// Only a generic signature opens the speculative region where the skip is allowed, so the non-generic +// overloads never reach the code under test. The generic pair at the bottom covers that. Skipping there made the object look like it had no string member and picked the // numeric candidate again, this time with the region wide open. declare function pick(x: { [k: string]: number }): "num"; ->pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 57)) ->x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 22)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 28)) +>pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 8, 57)) +>x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 8, 22)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 8, 28)) declare function pick(x: object): "obj"; ->pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 57)) ->x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 10, 22)) +>pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 8, 57)) +>x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 22)) const inferred = pick({ ->inferred : Symbol(inferred, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 12, 5)) ->pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 57)) +>inferred : Symbol(inferred, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 11, 5)) +>pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 8, 57)) get s() { ->s : Symbol(s, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 12, 23)) +>s : Symbol(s, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 11, 23)) return "hello"; }, }); const mustBeObj: "obj" = inferred; ->mustBeObj : Symbol(mustBeObj, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 17, 5)) ->inferred : Symbol(inferred, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 12, 5)) +>mustBeObj : Symbol(mustBeObj, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 16, 5)) +>inferred : Symbol(inferred, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 11, 5)) const annotated = pick({ ->annotated : Symbol(annotated, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 19, 5)) ->pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 57)) +>annotated : Symbol(annotated, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 18, 5)) +>pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 8, 57)) get s(): string { ->s : Symbol(s, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 19, 24)) +>s : Symbol(s, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 18, 24)) return "hello"; }, }); const alsoObj: "obj" = annotated; ->alsoObj : Symbol(alsoObj, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 24, 5)) ->annotated : Symbol(annotated, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 19, 5)) +>alsoObj : Symbol(alsoObj, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 23, 5)) +>annotated : Symbol(annotated, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 18, 5)) const numeric = pick({ ->numeric : Symbol(numeric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 26, 5)) ->pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 9, 57)) +>numeric : Symbol(numeric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 25, 5)) +>pick : Symbol(pick, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 0, 0), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 8, 57)) get n() { ->n : Symbol(n, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 26, 22)) +>n : Symbol(n, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 25, 22)) return 1; }, }); const mustBeNum: "num" = numeric; ->mustBeNum : Symbol(mustBeNum, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 31, 5)) ->numeric : Symbol(numeric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 26, 5)) +>mustBeNum : Symbol(mustBeNum, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 30, 5)) +>numeric : Symbol(numeric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 25, 5)) declare function generic(x: T): "num"; ->generic : Symbol(generic, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 31, 33), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 73)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 25)) ->k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 38)) ->x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 60)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 25)) +>generic : Symbol(generic, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 30, 33), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 32, 73)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 32, 25)) +>k : Symbol(k, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 32, 38)) +>x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 32, 60)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 32, 25)) declare function generic(x: T): "obj"; ->generic : Symbol(generic, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 31, 33), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 73)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 34, 25)) ->x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 34, 43)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 34, 25)) +>generic : Symbol(generic, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 30, 33), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 32, 73)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 25)) +>x : Symbol(x, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 43)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 25)) const inferredGeneric = generic({ ->inferredGeneric : Symbol(inferredGeneric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 36, 5)) ->generic : Symbol(generic, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 31, 33), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 73)) +>inferredGeneric : Symbol(inferredGeneric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 35, 5)) +>generic : Symbol(generic, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 30, 33), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 32, 73)) get s() { ->s : Symbol(s, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 36, 33)) +>s : Symbol(s, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 35, 33)) return "hello"; }, }); const genericMustBeObj: "obj" = inferredGeneric; ->genericMustBeObj : Symbol(genericMustBeObj, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 41, 5)) ->inferredGeneric : Symbol(inferredGeneric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 36, 5)) +>genericMustBeObj : Symbol(genericMustBeObj, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 40, 5)) +>inferredGeneric : Symbol(inferredGeneric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 35, 5)) const numericGeneric = generic({ ->numericGeneric : Symbol(numericGeneric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 43, 5)) ->generic : Symbol(generic, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 31, 33), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 33, 73)) +>numericGeneric : Symbol(numericGeneric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 42, 5)) +>generic : Symbol(generic, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 30, 33), Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 32, 73)) get n() { ->n : Symbol(n, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 43, 32)) +>n : Symbol(n, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 42, 32)) return 1; }, }); const genericMustBeNum: "num" = numericGeneric; ->genericMustBeNum : Symbol(genericMustBeNum, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 48, 5)) ->numericGeneric : Symbol(numericGeneric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 43, 5)) +>genericMustBeNum : Symbol(genericMustBeNum, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 47, 5)) +>numericGeneric : Symbol(numericGeneric, Decl(recursiveTypeThroughObjectLiteralGetterOverloads.ts, 42, 5)) diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.types index 1725e348cbcef..728fe45d05717 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.types +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.types @@ -5,9 +5,8 @@ // about the program, so skipping one on that alone makes an ordinary comparison depend on // resolution order. This picked the numeric overload for an object whose only member is a string. // -// The generic pair at the bottom matters more than the plain one: only a generic signature opens the -// speculative region where the skip is allowed at all, so the non-generic overloads never reach the -// code under test. Skipping there made the object look like it had no string member and picked the +// Only a generic signature opens the speculative region where the skip is allowed, so the non-generic +// overloads never reach the code under test. The generic pair at the bottom covers that. Skipping there made the object look like it had no string member and picked the // numeric candidate again, this time with the region wide open. declare function pick(x: { [k: string]: number }): "num"; diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.errors.txt b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.errors.txt index 8bc4502a50af7..56794ce97b7b0 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.errors.txt @@ -1,12 +1,11 @@ -recursiveTypeThroughObjectLiteralGetterReverseMapped.ts(43,7): error TS7022: 'category' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. -recursiveTypeThroughObjectLiteralGetterReverseMapped.ts(45,9): error TS7023: 'parent' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. +recursiveTypeThroughObjectLiteralGetterReverseMapped.ts(42,7): error TS7022: 'category' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. +recursiveTypeThroughObjectLiteralGetterReverseMapped.ts(44,9): error TS7023: 'parent' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. ==== recursiveTypeThroughObjectLiteralGetterReverseMapped.ts (2 errors) ==== // The same schema shape, but the parameter is written as a mapped type over the inferred one, so // inference runs through a reverse mapped type. Neither this change nor main resolves this variant -- - // both report TS7022/TS7023 on the declaration -- and the point of the case is that they report the - // SAME thing. + // both report TS7022/TS7023 on the declaration -- and the case asserts they report the same thing. // // A provisional comparison explores further than an ordinary check does, so it can walk into a // circularity that main never reaches because it collapses the getter first. Reporting that, or diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.symbols index 3e59cda5533df..23c232b2f1dff 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.symbols +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.symbols @@ -3,8 +3,7 @@ === recursiveTypeThroughObjectLiteralGetterReverseMapped.ts === // The same schema shape, but the parameter is written as a mapped type over the inferred one, so // inference runs through a reverse mapped type. Neither this change nor main resolves this variant -- -// both report TS7022/TS7023 on the declaration -- and the point of the case is that they report the -// SAME thing. +// both report TS7022/TS7023 on the declaration -- and the case asserts they report the same thing. // // A provisional comparison explores further than an ordinary check does, so it can walk into a // circularity that main never reaches because it collapses the getter first. Reporting that, or @@ -14,136 +13,136 @@ interface Internals { >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 0, 0)) ->O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 11, 20)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 10, 20)) optional: "true" | "false"; ->optional : Symbol(Internals.optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 11, 24)) +>optional : Symbol(Internals.optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 10, 24)) out: O; ->out : Symbol(Internals.out, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 12, 31)) ->O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 11, 20)) +>out : Symbol(Internals.out, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 11, 31)) +>O : Symbol(O, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 10, 20)) } interface StringSchema extends Internals { ->StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 14, 1)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 13, 1)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 0, 0)) optional: "false"; ->optional : Symbol(StringSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 16, 50)) +>optional : Symbol(StringSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 15, 50)) } type Shape = Record; ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 18, 1)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 17, 1)) >Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) type Prettify = { [K in keyof T]: T[K] } & {}; ->Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 20, 33)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 14)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 22)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 14)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 14)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 22)) +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 19, 33)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 20, 14)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 20, 22)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 20, 14)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 20, 14)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 20, 22)) type ObjectOut = Prettify< ->ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 49)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 18, 1)) ->Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 20, 33)) +>ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 20, 49)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 15)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 17, 1)) +>Prettify : Symbol(Prettify, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 19, 33)) { [K in keyof S as S[K] extends { optional: "true" } ? K : never]?: S[K]["out"]; ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 24, 9)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 24, 9)) ->optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 24, 39)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 24, 9)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 24, 9)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 23, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 15)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 23, 9)) +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 23, 39)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 23, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 23, 9)) } & { [K in keyof S as S[K] extends { optional: "true" } ? never : K]: S[K]["out"]; ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 26, 9)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 26, 9)) ->optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 26, 39)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 26, 9)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 22, 15)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 26, 9)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 25, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 15)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 25, 9)) +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 25, 39)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 25, 9)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 15)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 25, 9)) } >; interface ObjectSchema extends Internals> { ->ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 28, 2)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 30, 23)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 18, 1)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 27, 2)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 29, 23)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 17, 1)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 0, 0)) ->ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 21, 49)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 30, 23)) +>ObjectOut : Symbol(ObjectOut, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 20, 49)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 29, 23)) optional: "false"; ->optional : Symbol(ObjectSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 30, 73)) +>optional : Symbol(ObjectSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 29, 73)) } interface OptionalSchema> extends Internals { ->OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 32, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 34, 25)) +>OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 31, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 33, 25)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 0, 0)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 0, 0)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 34, 25)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 33, 25)) optional: "true"; ->optional : Symbol(OptionalSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 34, 92)) +>optional : Symbol(OptionalSchema.optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 33, 92)) } declare function object(shape: { [K in keyof S]: S[K] }): ObjectSchema; ->object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 36, 1)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 24)) ->Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 18, 1)) ->shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 41)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 51)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 24)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 24)) ->K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 51)) ->ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 28, 2)) ->S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 24)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 35, 1)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 37, 24)) +>Shape : Symbol(Shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 17, 1)) +>shape : Symbol(shape, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 37, 41)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 37, 51)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 37, 24)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 37, 24)) +>K : Symbol(K, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 37, 51)) +>ObjectSchema : Symbol(ObjectSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 27, 2)) +>S : Symbol(S, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 37, 24)) declare function text(): StringSchema; ->text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 91)) ->StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 14, 1)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 37, 91)) +>StringSchema : Symbol(StringSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 13, 1)) declare function optional>(schema: T): OptionalSchema; ->optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 39, 38)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 40, 26)) +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 38)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 39, 26)) >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 0, 0)) ->schema : Symbol(schema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 40, 52)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 40, 26)) ->OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 32, 1)) ->T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 40, 26)) +>schema : Symbol(schema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 39, 52)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 39, 26)) +>OptionalSchema : Symbol(OptionalSchema, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 31, 1)) +>T : Symbol(T, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 39, 26)) const category = object({ ->category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 42, 5)) ->object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 36, 1)) +>category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 41, 5)) +>object : Symbol(object, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 35, 1)) name: text(), ->name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 42, 25)) ->text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 91)) +>name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 41, 25)) +>text : Symbol(text, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 37, 91)) get parent() { ->parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 43, 17)) +>parent : Symbol(parent, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 42, 17)) return optional(category); ->optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 39, 38)) ->category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 42, 5)) +>optional : Symbol(optional, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 38, 38)) +>category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 41, 5)) }, }); declare const sample: (typeof category)["out"]; ->sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 49, 13)) ->category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 42, 5)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 48, 13)) +>category : Symbol(category, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 41, 5)) const topName: string = sample.name; ->topName : Symbol(topName, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 50, 5)) ->sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 49, 13)) +>topName : Symbol(topName, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 49, 5)) +>sample : Symbol(sample, Decl(recursiveTypeThroughObjectLiteralGetterReverseMapped.ts, 48, 13)) diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.types index 6180bf9b3a638..b09401ca310d7 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.types +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.types @@ -3,8 +3,7 @@ === recursiveTypeThroughObjectLiteralGetterReverseMapped.ts === // The same schema shape, but the parameter is written as a mapped type over the inferred one, so // inference runs through a reverse mapped type. Neither this change nor main resolves this variant -- -// both report TS7022/TS7023 on the declaration -- and the point of the case is that they report the -// SAME thing. +// both report TS7022/TS7023 on the declaration -- and the case asserts they report the same thing. // // A provisional comparison explores further than an ordinary check does, so it can walk into a // circularity that main never reaches because it collapses the getter first. Reporting that, or diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.symbols index a075c8b405b91..7d7f4eaba31bf 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.symbols +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.symbols @@ -6,8 +6,8 @@ // recursiveTypeThroughObjectLiteralGetterUnionOptional.ts is the same shape with the member optional. // // The .types baseline prints the recursive member as `any` once it is a few levels deep. That is the -// type printer truncating, not the type -- the member accesses at the bottom are what pin the real -// shape, and the @ts-expect-error is what would catch an `any` standing in for it. +// type printer truncating, not the type. The member accesses at the bottom pin the real shape, and +// the @ts-expect-error would catch an `any` standing in for it. interface Internals { >Internals : Symbol(Internals, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 0, 0)) @@ -170,7 +170,7 @@ const deepName: string = sample.child.child.name; >child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 36, 17)) >name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 35, 25)) -// An `any` here would swallow this, so the expected error is what proves the member is a real object. +// An `any` would swallow this; the expected error shows the member is a real object. // @ts-expect-error sample.child.missing; >sample.child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnion.ts, 36, 17)) diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.types index 6e898c835cb48..6392517a38ce0 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.types +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnion.types @@ -6,8 +6,8 @@ // recursiveTypeThroughObjectLiteralGetterUnionOptional.ts is the same shape with the member optional. // // The .types baseline prints the recursive member as `any` once it is a few levels deep. That is the -// type printer truncating, not the type -- the member accesses at the bottom are what pin the real -// shape, and the @ts-expect-error is what would catch an `any` standing in for it. +// type printer truncating, not the type. The member accesses at the bottom pin the real shape, and +// the @ts-expect-error would catch an `any` standing in for it. interface Internals { readonly out: O; @@ -110,7 +110,7 @@ const deepName: string = sample.child.child.name; >child : { name: string; readonly child: any; } >name : string -// An `any` here would swallow this, so the expected error is what proves the member is a real object. +// An `any` would swallow this; the expected error shows the member is a real object. // @ts-expect-error sample.child.missing; >sample.child.missing : error diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.symbols index 65d323228a4cd..29923fe86a8f7 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.symbols +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.symbols @@ -182,7 +182,7 @@ const nestedName: string | undefined = sample.child?.name; >child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 37, 17)) >name : Symbol(name, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 36, 25)) -// An `any` here would swallow this, so the expected error is what proves the member is a real object. +// An `any` would swallow this; the expected error shows the member is a real object. // @ts-expect-error sample.child?.missing; >sample.child : Symbol(child, Decl(recursiveTypeThroughObjectLiteralGetterUnionOptional.ts, 37, 17)) diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.types index ba18bc2175d56..928ad167fc805 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.types +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.types @@ -104,7 +104,7 @@ const nestedName: string | undefined = sample.child?.name; >child : { name: string; readonly child: any | undefined; } | undefined >name : string | undefined -// An `any` here would swallow this, so the expected error is what proves the member is a real object. +// An `any` would swallow this; the expected error shows the member is a real object. // @ts-expect-error sample.child?.missing; >sample.child?.missing : error diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.symbols b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.symbols index 62a52e0a2efd8..08885702a240a 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.symbols +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.symbols @@ -227,8 +227,8 @@ const secondB: string = fromSecond.me.b; >me : Symbol(me, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 53, 32)) >b : Symbol(b, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 53, 23)) -// Every assertion above still passes if the type degraded to `any`. These do not: each names a key -// the schema does not have, so the expected error is what shows the member is a real object type. +// Every assertion above still passes if the type degraded to `any`. These do not; each names a key the +// schema does not have. // @ts-expect-error fromClass.self.missing; >fromClass.self : Symbol(self, Decl(recursiveTypeThroughObjectLiteralGetterVariations.ts, 15, 19)) diff --git a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.types b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.types index 8a78711b3c951..66342fe1a6abe 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.types +++ b/tsc/testdata/baselines/reference/compiler/recursiveTypeThroughObjectLiteralGetterVariations.types @@ -225,8 +225,8 @@ const secondB: string = fromSecond.me.b; >me : { b: string; readonly me: any; } >b : string -// Every assertion above still passes if the type degraded to `any`. These do not: each names a key -// the schema does not have, so the expected error is what shows the member is a real object type. +// Every assertion above still passes if the type degraded to `any`. These do not; each names a key the +// schema does not have. // @ts-expect-error fromClass.self.missing; >fromClass.self.missing : error diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetter.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetter.ts index 45743d0ca6167..28791679f257d 100644 --- a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetter.ts +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetter.ts @@ -46,9 +46,9 @@ declare const sample: TreeNode; const leafName: string = sample.name; const nestedName: string = sample.children[0].children[0].name; -// Those two pass on their own if the whole thing collapsed to `any`, which is exactly the failure -// this test exists to catch, so the recursion point is also probed where `any` cannot hide: an -// unknown key has to be an error, and a known one has to have the type it is declared with. +// Both assertions above pass if the whole thing collapsed to `any`, so the recursion point is also +// probed where `any` cannot hide. An unknown key must be an error, and a known one must carry its +// declared type. // @ts-expect-error sample.children[0].missing; // @ts-expect-error diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts index 0d628bc094406..87cb803d9a83a 100644 --- a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterAbsentDuringInference.ts @@ -2,18 +2,16 @@ // @noEmit: true // The companion to recursiveTypeThroughObjectLiteralGetterAbsentProperty, on the speculative path. -// Inferring `T` from `Wrapper` opens a region, and checking the inferred type against the -// constraint reads `D` while its base is still being instantiated. A conditional asked about `D` in -// that window used to be told every property matched, so `[D] extends [Need]` took the true branch -// and the index signature became `{ run(): void }` -- `d.anything.run()` then went unreported. +// Inferring `T` from `Wrapper` opens a region, and checking the inferred type against the constraint +// reads `D` while its base is still being instantiated. A conditional asked about `D` in that window used +// to be told every property matched, so `[D] extends [Need]` took the true branch, the index signature +// became `{ run(): void }`, and `d.anything.run()` went unreported. // -// The window withholds inherited members and nothing else, so `missing` -- which no base of `D` -// declares -- is genuinely absent even there. The conditional has to take the same branch it would -// take once the table is complete, and the call has to stay an error. +// The window withholds inherited members and nothing else, so `missing`, which no base of `D` declares, +// is absent even there. The conditional takes the branch it would take once the table is complete. // -// The opposite direction -- a name a base does declare, which has to stay withheld -- is guarded by -// the two hover tests for #62181 and by the mapped-type case, all three of which fail if the miss is -// reported instead. A case written here for it would not reach the skip at all. +// The opposite direction, a name a base does declare and which stays withheld, is covered by the two +// #62181 hover tests and the mapped-type case; a case written here would not reach the skip. export function run(): void { force(w); diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.ts index d03d6387de600..2a080e89a7ef3 100644 --- a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.ts +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterConstraint.ts @@ -3,8 +3,8 @@ // Skipping an accessor during a check that reports nothing is a deferral, not a waiver. An array of // schemas is not a Schema, so this reports. The baseline is byte-identical to what an unpatched -// compiler produces, and that is the whole point of the case: it pins that the constraint is still -// enforced, not which pass enforced it. The recursive shape where the deferred pass is the only thing +// compiler produces, pinning that the constraint is still enforced rather than which pass enforced +// it. The recursive shape where the deferred pass is the only thing // that can report it is recursiveTypeThroughObjectLiteralGetterPostponedConstraint.ts. interface Schema { diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.ts index 7b5cba6d5d01d..345c6465e4ddc 100644 --- a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.ts +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterFlowLoop.ts @@ -3,16 +3,14 @@ // A recursive getter whose body runs a loop, assigning to a union-typed variable. // -// The loop puts entries on the flow-loop stack, and the assignment's declared type being a union is -// what makes control flow analysis evaluate the assigned expression rather than take the declared -// type. So the inner getter is forced from inside a loop back-edge, while a provisional comparison is -// open. +// The loop puts entries on the flow-loop stack, and the union declared type makes control flow analysis +// evaluate the assigned expression rather than take the declared type, so the inner getter is forced +// from inside a loop back-edge while a provisional comparison is open. // -// This is the case that separates saving a stack's length from saving the stack itself. -// `checkExpressionCachedEx` swaps in a nil flow-loop stack and puts the original back only when it -// returns normally, so an unwind that crosses it leaves the nil in place. Restoring by length then -// re-slices the replacement instead of the saved stack, which is a runtime panic rather than a -// diagnostic. The three stacks some caller replaces wholesale are therefore held as slice headers. +// checkExpressionCachedEx swaps in a nil flow-loop stack and restores it only on a normal return, so an +// unwind crossing it leaves the nil in place. Restoring by length then re-slices the replacement rather +// than the saved stack, which is a panic rather than a diagnostic. The three stacks a caller replaces +// wholesale are held as slice headers for this reason. interface Internals { readonly out: O; @@ -62,7 +60,6 @@ const node = object({ declare const sample: Out; const topName: string = sample.name; -// A key the shape does not declare is still absent, which is what tells a resolved type apart from -// one that collapsed to `any`. +// A key the shape does not declare is still absent; a collapse to `any` would permit this. // @ts-expect-error sample.missing; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts index 01b91cb73e63f..9b2bb7e9d1783 100644 --- a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterInheritedMember.ts @@ -1,21 +1,16 @@ // @strict: true // @noEmit: true -// The shape from #62180, where the recursive member is reached through a mapped type that remaps its -// keys by reading each member's own discriminant. Resolving the object schema's members instantiates -// its base with a type that names the schema back, so the schema's table is read while it is still -// being assembled and its inherited `out` is not in it yet. +// The shape from #62180. The recursive member is reached through a mapped type that remaps keys by +// reading each member's discriminant, so resolving the object schema instantiates its base with a type +// naming the schema back, and the table is read before its inherited `out` is added. // -// A lookup that misses in that window finishes itself against the base types still to be inherited, -// so it returns the member the table is about to hold. An earlier draft instead answered such a miss -// with `any`, which cleared the error here and was wrong everywhere else: a self-referential -// interface reaches the same window with no getter and no inference anywhere in the program, and an -// unpatched compiler answers it from the index signature, so `any` silently accepted an indexed -// access that every other compiler rejects. Completing the lookup is what separates the two -- a name -// no base carries is still absent. +// A miss there resolves against the bases still to be inherited. Answering `any` instead would clear +// this error and break the self-referential interface case, where an unpatched compiler answers from the +// index signature and `any` would accept an indexed access every other compiler rejects. // -// The `any` that appears against `parent` in the .types baseline is the printer eliding a recursive -// reference, not an unresolved member. The assertions below are what distinguish those two. +// The `any` against `parent` in the .types baseline is printer elision, not an unresolved member; the +// assertions below tell those apart. interface Internals { optional: "true" | "false"; @@ -56,21 +51,19 @@ const category = object({ }); declare const sample: (typeof category)["out"]; -// The key that does not recurse resolves fully, and the remapping put the recursive one in the -// optional bucket, so it is `parent?:` rather than `parent:`. Both of those are new: main gives the -// whole declaration an implicit `any` and reports it. +// The non-recursive key resolves fully, and the remapping put the recursive one in the optional bucket, +// so it is `parent?:`. Both are new; main gives the whole declaration an implicit `any`. const topName: string = sample.name; -// The recursive one resolves too, and keeps resolving as far down as it is walked. +// The recursive one resolves too, at any depth walked. const parentName: string = sample.parent!.name; const deepName: string = sample.parent!.parent!.parent!.name; -// Depth is where a collapse would hide: `any` would accept both of these, so they are what keeps the -// comment above honest. +// A collapse to `any` would accept both of these. // @ts-expect-error const wrongType: number = sample.parent!.parent!.name; // @ts-expect-error sample.parent!.parent!.missing; -// A property that is genuinely absent must still be reported, whoever is asking. +// A genuinely absent property is still reported. interface Base { readonly own: number; } interface Derived extends Base { readonly extra: string; } declare const derived: Derived; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterMutual.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterMutual.ts index a27c639456613..acf6a78ec29e1 100644 --- a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterMutual.ts +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterMutual.ts @@ -1,18 +1,15 @@ // @strict: true // @noEmit: true -// Two schemas that name each other through getters, with the object's members reached through two -// separate key remappings -- one for the output side and one for the input side, each keyed on a -// different member of the schema's own internals. +// Two schemas naming each other through getters, with the object's members reached through two separate +// key remappings, one per variance side, each keyed on a different member of the internals. // -// Both remappings read the member table of a type whose bases are still being inherited, and they -// read it at different points in that assembly. That is what makes this shape the one that separates -// the two answers a miss in that window can have. A speculative comparison has to decline, because -// the member it would force is the one its own question is about. An ordinary lookup has to finish -// itself against the base types still to be inherited, which is what keeps the window unobservable. -// Giving a speculative miss the second answer forces `posts` while its own return type is still -// being inferred, and the getter falls back to an implicit `any` -- so the two cannot be merged into -// one answer, and a single remapping does not reach the case. +// Both remappings read the member table of a type whose bases are still being inherited, at different +// points in that assembly, which is what makes this shape separate the two answers a miss can have. A +// speculative comparison must decline, since the member it would force is the subject of its own +// question. An ordinary lookup must resolve against the bases still to be inherited. Giving a speculative +// miss the second answer forces `posts` mid-inference and the getter falls back to an implicit `any`. A +// single remapping does not reach this. interface Internals { output: O; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.ts index b4febc8f6167a..63a137ddba564 100644 --- a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.ts +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterOverloads.ts @@ -5,9 +5,8 @@ // about the program, so skipping one on that alone makes an ordinary comparison depend on // resolution order. This picked the numeric overload for an object whose only member is a string. // -// The generic pair at the bottom matters more than the plain one: only a generic signature opens the -// speculative region where the skip is allowed at all, so the non-generic overloads never reach the -// code under test. Skipping there made the object look like it had no string member and picked the +// Only a generic signature opens the speculative region where the skip is allowed, so the non-generic +// overloads never reach the code under test. The generic pair at the bottom covers that. Skipping there made the object look like it had no string member and picked the // numeric candidate again, this time with the region wide open. declare function pick(x: { [k: string]: number }): "num"; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.ts index c533a65fce85d..e20779f185e47 100644 --- a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.ts +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterReverseMapped.ts @@ -3,8 +3,7 @@ // The same schema shape, but the parameter is written as a mapped type over the inferred one, so // inference runs through a reverse mapped type. Neither this change nor main resolves this variant -- -// both report TS7022/TS7023 on the declaration -- and the point of the case is that they report the -// SAME thing. +// both report TS7022/TS7023 on the declaration -- and the case asserts they report the same thing. // // A provisional comparison explores further than an ordinary check does, so it can walk into a // circularity that main never reaches because it collapses the getter first. Reporting that, or diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnion.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnion.ts index dd1cf74223c1b..20ce4739781ce 100644 --- a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnion.ts +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnion.ts @@ -6,8 +6,8 @@ // recursiveTypeThroughObjectLiteralGetterUnionOptional.ts is the same shape with the member optional. // // The .types baseline prints the recursive member as `any` once it is a few levels deep. That is the -// type printer truncating, not the type -- the member accesses at the bottom are what pin the real -// shape, and the @ts-expect-error is what would catch an `any` standing in for it. +// type printer truncating, not the type. The member accesses at the bottom pin the real shape, and +// the @ts-expect-error would catch an `any` standing in for it. interface Internals { readonly out: O; @@ -51,6 +51,6 @@ declare const sample: TreeOut; const topName: string = sample.name; const nestedName: string = sample.child.name; const deepName: string = sample.child.child.name; -// An `any` here would swallow this, so the expected error is what proves the member is a real object. +// An `any` would swallow this; the expected error shows the member is a real object. // @ts-expect-error sample.child.missing; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.ts index 117b5c7ec2c8b..5c7c2c3c0d466 100644 --- a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.ts +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterUnionOptional.ts @@ -49,6 +49,6 @@ declare const sample: Out; // The recursive member resolves: two levels in, `name` is still a string. const topName: string = sample.name; const nestedName: string | undefined = sample.child?.name; -// An `any` here would swallow this, so the expected error is what proves the member is a real object. +// An `any` would swallow this; the expected error shows the member is a real object. // @ts-expect-error sample.child?.missing; diff --git a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterVariations.ts b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterVariations.ts index 4f7f94d65d9b0..12b8981feed09 100644 --- a/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterVariations.ts +++ b/tsc/testdata/tests/cases/compiler/recursiveTypeThroughObjectLiteralGetterVariations.ts @@ -60,8 +60,8 @@ declare const fromSecond: typeof second.out; const firstA: string = fromFirst.me.a; const secondB: string = fromSecond.me.b; -// Every assertion above still passes if the type degraded to `any`. These do not: each names a key -// the schema does not have, so the expected error is what shows the member is a real object type. +// Every assertion above still passes if the type degraded to `any`. These do not; each names a key the +// schema does not have. // @ts-expect-error fromClass.self.missing; // @ts-expect-error