code: private FlowScope traverseDestructuringPatternHelper(
        Node pattern, FlowScope scope, JSType patternType, TypeDeclaringCallback declarer) {
      checkArgument(pattern.isDestructuringPattern(), pattern);
      checkNotNull(patternType);
      for (DestructuredTarget target :
          DestructuredTarget.createAllNonEmptyTargetsInPattern(registry, patternType, pattern)) {
  
        // The computed property is always evaluated first.
        if (target.hasComputedProperty()) {
          scope = traverse(target.getComputedProperty().getFirstChild(), scope);
        }
        Node targetNode = target.getNode();
  
        if (targetNode.isDestructuringPattern()) {
          if (target.hasDefaultValue()) {
            traverse(target.getDefaultValue(), scope);
          }
  
          // traverse into nested patterns
          JSType targetType = target.inferType();
          targetType = targetType != null ? targetType : getNativeType(UNKNOWN_TYPE);
          scope = traverseDestructuringPatternHelper(targetNode, scope, targetType, declarer);
        } else {
          scope = traverse(targetNode, scope);
  
          if (target.hasDefaultValue()) {
            // TODO(lharker): what do we do with the inferred slots in the scope?
            // throw them away or join them with the previous scope?
            traverse(target.getDefaultValue(), scope);
          }
  
          // declare in the scope
          scope = declarer.declareTypeInScope(scope, targetNode, target.inferType());
        }
      }
      // put the `inferred type` of a pattern on it, to make it easier to do typechecking
      pattern.setJSType(patternType);
      return scope;
    }
docs: Traverses a destructuring pattern, and calls {@code declarer.declareTypeInScope} on each lvalue
  
  <p>The purpose of the callback is to abstract different logic for declaring lvalues in function
  parameters vs. in regular assignments/declarations.
  
  @param declarer contains a callback called on every lvalue.