Repository: microsoft/TypeScript
File: src/compiler/binder.ts (TypeScript)

Code before:
        const classPrototype = lhs.expression as BindableStaticAccessExpression;
        const constructorFunction = classPrototype.expression;

        // Fix up parent pointers since we're going to use these nodes before we bind into them
        setParent(constructorFunction, classPrototype);
        setParent(classPrototype, lhs);
        setParent(lhs, parent);

        bindPropertyAssignment(constructorFunction, lhs, /*isPrototypeProperty*/ true, /*containerIsClass*/ true);
    }

    function bindObjectDefinePropertyAssignment(node: BindableObjectDefinePropertyCall) {
        let namespaceSymbol = lookupSymbolForPropertyAccess(node.arguments[0]);
        const isToplevel = node.parent.parent.kind === SyntaxKind.SourceFile;
        namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, node.arguments[0], isToplevel, /*isPrototypeProperty*/ false, /*containerIsClass*/ false);
        bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ false);
    }

    function bindSpecialPropertyAssignment(node: BindablePropertyAssignmentExpression) {
        // Class declarations in Typescript do not allow property declarations
        const parentSymbol = lookupSymbolForPropertyAccess(node.left.expression, container) || lookupSymbolForPropertyAccess(node.left.expression, blockScopeContainer) ;
        if (!isInJSFile(node) && !isFunctionSymbol(parentSymbol)) {
            return;
        }
        const rootExpr = getLeftmostAccessExpression(node.left);
        if (isIdentifier(rootExpr) && lookupSymbolForName(container, rootExpr.escapedText)!.flags & SymbolFlags.Alias) {
            return;
        }
        // Fix up parent pointers since we're going to use these nodes before we bind into them
        setParent(node.left, node);
        setParent(node.right, node);
        if (isIdentifier(node.left.expression) && container === file && isExportsOrModuleExportsOrAlias(file, node.left.expression)) {
            // This can be an alias for the 'exports' or 'module.exports' names, e.g.
            //    var util = module.exports;
            //    util.property = function ...
            bindExportsPropertyAssignment(node as BindableStaticPropertyAssignmentExpression);
        }
        else if (hasDynamicName(node)) {
            bindAnonymousDeclaration(node, SymbolFlags.Property | SymbolFlags.Assignment, InternalSymbolName.Computed);
            const sym = bindPotentiallyMissingNamespaces(parentSymbol, node.left.expression, isTopLevelNamespaceAssignment(node.left), /*isPrototypeProperty*/ false, /*containerIsClass*/ false);
            addLateBoundAssignmentDeclarationToSymbol(node, sym);
        }
        else {
            bindStaticPropertyAssignment(cast(node.left, isBindableStaticNameExpression));
        }
    }

    /**
     * For nodes like `x.y = z`, declare a member 'y' on 'x' if x is a function (or IIFE) or class or {}, or not declared.
     * Also works for expression statements preceded by JSDoc, like / ** @type number * / x.y;
     */

Review comment (bug):
[@typescript-eslint/no-non-null-asserted-optional-chain](https://typescript-eslint.io/rules/no-non-null-asserted-optional-chain): _"Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong."_

The `?.` followed by a `!` indicates the value must exist, so it's more succinct to use a single `!`.

Code after:
        // Look up the function in the local scope, since prototype assignments should
        // follow the function declaration
        const classPrototype = lhs.expression as BindableStaticAccessExpression;
        const constructorFunction = classPrototype.expression;

        // Fix up parent pointers since we're going to use these nodes before we bind into them
        setParent(constructorFunction, classPrototype);
        setParent(classPrototype, lhs);
        setParent(lhs, parent);

        bindPropertyAssignment(constructorFunction, lhs, /*isPrototypeProperty*/ true, /*containerIsClass*/ true);
    }

    function bindObjectDefinePropertyAssignment(node: BindableObjectDefinePropertyCall) {
        let namespaceSymbol = lookupSymbolForPropertyAccess(node.arguments[0]);
        const isToplevel = node.parent.parent.kind === SyntaxKind.SourceFile;
        namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, node.arguments[0], isToplevel, /*isPrototypeProperty*/ false, /*containerIsClass*/ false);
        bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ false);
    }

    function bindSpecialPropertyAssignment(node: BindablePropertyAssignmentExpression) {
        // Class declarations in Typescript do not allow property declarations
        const parentSymbol = lookupSymbolForPropertyAccess(node.left.expression, container) || lookupSymbolForPropertyAccess(node.left.expression, blockScopeContainer) ;
        if (!isInJSFile(node) && !isFunctionSymbol(parentSymbol)) {
            return;
        }
        const rootExpr = getLeftmostAccessExpression(node.left);
        if (isIdentifier(rootExpr) && lookupSymbolForName(container, rootExpr.escapedText)?.flags! & SymbolFlags.Alias) {
            return;
        }
        // Fix up parent pointers since we're going to use these nodes before we bind into them
        setParent(node.left, node);
        setParent(node.right, node);
        if (isIdentifier(node.left.expression) && container === file && isExportsOrModuleExportsOrAlias(file, node.left.expression)) {
            // This can be an alias for the 'exports' or 'module.exports' names, e.g.
            //    var util = module.exports;
            //    util.property = function ...
            bindExportsPropertyAssignment(node as BindableStaticPropertyAssignmentExpression);
        }
        else if (hasDynamicName(node)) {
            bindAnonymousDeclaration(node, SymbolFlags.Property | SymbolFlags.Assignment, InternalSymbolName.Computed);
            const sym = bindPotentiallyMissingNamespaces(parentSymbol, node.left.expression, isTopLevelNamespaceAssignment(node.left), /*isPrototypeProperty*/ false, /*containerIsClass*/ false);
            addLateBoundAssignmentDeclarationToSymbol(node, sym);
        }
        else {
            bindStaticPropertyAssignment(cast(node.left, isBindableStaticNameExpression));
        }
    }

    /**
     * For nodes like `x.y = z`, declare a member 'y' on 'x' if x is a function (or IIFE) or class or {}, or not declared.