@@ -515,6 +515,14 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
515515 program . reportDiagnostic ( x ) ;
516516 } ;
517517
518+ // State for deferred member resolution: when a member access targets a container
519+ // that is still being checked (creating=true), we store the info here so that
520+ // the calling checkModelProperty can register a waitingForResolution callback
521+ // instead of emitting an error.
522+ let pendingMemberResolution :
523+ | { containerType : Type ; node : MemberExpressionNode ; baseSym : Sym }
524+ | undefined ;
525+
518526 const typePrototype : TypePrototype = { } ;
519527 const globalNamespaceType = createGlobalNamespaceType ( ) ;
520528
@@ -4397,6 +4405,13 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
43974405 }
43984406 }
43994407
4408+ // If the late-bound member resolution detected a creating container, the member may
4409+ // become available when the container finishes. Don't emit an error — let the caller
4410+ // handle deferred resolution via waitingForResolution callback.
4411+ if ( pendingMemberResolution ) {
4412+ return undefined ;
4413+ }
4414+
44004415 if ( base . flags & SymbolFlags . Namespace ) {
44014416 reportCheckerDiagnostic (
44024417 createDiagnostic ( {
@@ -4469,7 +4484,19 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
44694484 }
44704485
44714486 // Don't force-check if the container is currently being resolved (cycle).
4487+ // Record pending info so the caller can defer resolution via waitingForResolution.
44724488 if ( pendingResolutions . has ( base , ResolutionKind . Type ) ) {
4489+ const containerType = links . declaredType ;
4490+ if ( containerType ?. creating ) {
4491+ pendingMemberResolution = { containerType, node, baseSym : base } ;
4492+ }
4493+ return undefined ;
4494+ }
4495+
4496+ // If the container type is already being checked (creating), we can't force-resolve
4497+ // its members yet. Record pending info for deferred resolution.
4498+ if ( links . declaredType ?. creating ) {
4499+ pendingMemberResolution = { containerType : links . declaredType , node, baseSym : base } ;
44734500 return undefined ;
44744501 }
44754502
@@ -6999,6 +7026,25 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
69997026
70007027 type . type = getTypeForNode ( prop . value , ctx ) ;
70017028
7029+ // If the type resolved to errorType because a member access targeted a container
7030+ // that was still being checked, register a callback to update the property type
7031+ // when the container finishes.
7032+ const pending = pendingMemberResolution ;
7033+ pendingMemberResolution = undefined ;
7034+ if ( pending && type . type === errorType ) {
7035+ ensureResolved ( [ pending . containerType ] , type , ( ) => {
7036+ const resolvedType = tryForceResolveLateBoundMember ( ctx , pending . baseSym , pending . node ) ;
7037+ if ( resolvedType ) {
7038+ if ( resolvedType . flags & SymbolFlags . LateBound ) {
7039+ compilerAssert ( resolvedType . type , "Expected late bound symbol to have type" ) ;
7040+ type . type = resolvedType . type as Type ;
7041+ } else {
7042+ type . type = getTypeForNode ( getSymNode ( resolvedType ) ! , ctx ) ;
7043+ }
7044+ }
7045+ } ) ;
7046+ }
7047+
70027048 // Detect property-to-property cycles (e.g., A.a -> B.a -> A.a)
70037049 if ( hasPropertyTypeCycle ( type ) ) {
70047050 if ( ctx . mapper === undefined ) {
@@ -7439,7 +7485,7 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
74397485 */
74407486 function checkAugmentDecorator ( ctx : CheckContext , node : AugmentDecoratorStatementNode ) {
74417487 // This will validate the target type is pointing to a valid ref.
7442- resolveTypeReferenceSym ( ctx . withMapper ( undefined ) , node . targetType , {
7488+ const targetSym = resolveTypeReferenceSym ( ctx . withMapper ( undefined ) , node . targetType , {
74437489 resolveDeclarationOfTemplate : true ,
74447490 } ) ;
74457491
@@ -7482,6 +7528,28 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
74827528 ) ;
74837529 }
74847530
7531+ // If the target sym was resolved at checker time (e.g. late-bound member from template)
7532+ // but wasn't registered during name resolution, apply the decorator directly.
7533+ if ( targetSym && targetSym . flags & SymbolFlags . LateBound ) {
7534+ const augmentDecsForSym = resolver . getAugmentDecoratorsForSym ( targetSym ) ;
7535+ if ( ! augmentDecsForSym . includes ( node ) ) {
7536+ const targetType = targetSym . type && isType ( targetSym . type ) ? targetSym . type : undefined ;
7537+ if ( targetType && "decorators" in targetType ) {
7538+ const decorator = checkDecoratorApplication ( ctx , targetType , node ) ;
7539+ if ( decorator ) {
7540+ targetType . decorators . unshift ( decorator ) ;
7541+ const validators = applyDecoratorToType ( program , decorator , targetType ) ;
7542+ if ( validators ?. onTargetFinish ) {
7543+ program . reportDiagnostics ( validators . onTargetFinish ( ) ) ;
7544+ }
7545+ if ( validators ?. onGraphFinish ) {
7546+ postCheckValidators . push ( validators . onGraphFinish ) ;
7547+ }
7548+ }
7549+ }
7550+ }
7551+ }
7552+
74857553 // If this was used to get a type this is invalid, only used for validation.
74867554 return errorType ;
74877555 }
0 commit comments