private JSType getNominalTypeHelper(
     RawNominalType rawType,
     Node n,
     DeclaredTypeRegistry registry,
     ImmutableList<String> outerTypeParameters)
     throws UnknownTypeException {
   NominalType uninstantiated = rawType.getAsNominalType();
   if (!rawType.isGeneric() && !n.hasChildren()) {
     return rawType.getInstanceWithNullability(NULLABLE_TYPES_BY_DEFAULT);
   }
   ImmutableList.Builder<JSType> typeList = ImmutableList.builder();
   if (n.hasChildren()) {
     // Compute instantiation of polymorphic class/interface.
     Preconditions.checkState(n.getFirstChild().isBlock(), n);
     for (Node child : n.getFirstChild().children()) {
       typeList.add(getTypeFromCommentHelper(child, registry, outerTypeParameters));
     }
   }
   ImmutableList<JSType> typeArguments = typeList.build();
   ImmutableList<String> typeParameters = rawType.getTypeParameters();
   int typeArgsSize = typeArguments.size();
   int typeParamsSize = typeParameters.size();
   if (typeArgsSize != typeParamsSize) {
     // We used to also warn when (typeArgsSize < typeParamsSize), but it
     // happens so often that we stopped. Array, Object and goog.Promise are
     // common culprits, but many other types as well.
     if (typeArgsSize > typeParamsSize) {
       warnings.add(
           JSError.make(
               n,
               INVALID_GENERICS_INSTANTIATION,
               uninstantiated.getName(),
               String.valueOf(typeParamsSize),
               String.valueOf(typeArgsSize)));
     }
     return maybeMakeNullable(
         JSType.fromObjectType(
             ObjectType.fromNominalType(
                 uninstantiated.instantiateGenerics(
                     fixLengthOfTypeList(typeParameters.size(), typeArguments)))));
   }
   return maybeMakeNullable(
       JSType.fromObjectType(
           ObjectType.fromNominalType(uninstantiated.instantiateGenerics(typeArguments))));
 }
 StringBuilder appendTo(StringBuilder builder) {
   if (!hasNonPrototypeProperties()) {
     if (fn != null) {
       return fn.appendTo(builder);
     } else if (getNominalType() != null) {
       return getNominalType().appendTo(builder);
     }
   }
   if (nominalType != null && !nominalType.getName().equals("Function")) {
     nominalType.appendTo(builder);
   } else if (isStruct()) {
     builder.append("struct");
   } else if (isDict()) {
     builder.append("dict");
   }
   if (fn != null) {
     builder.append("<|");
     fn.appendTo(builder);
     builder.append("|>");
   }
   if (nominalType == null || !props.isEmpty()) {
     builder.append('{');
     boolean firstIteration = true;
     for (String pname : new TreeSet<>(props.keySet())) {
       if (firstIteration) {
         firstIteration = false;
       } else {
         builder.append(", ");
       }
       builder.append(pname);
       builder.append(':');
       props.get(pname).appendTo(builder);
     }
     builder.append('}');
   }
   if (isLoose) {
     builder.append(" (loose)");
   }
   return builder;
 }