public static ParameterizedType parameterizedType(Class<?> rawType, Type... typeArguments) {
    var typeParamsCount = rawType.getTypeParameters().length;
    if (typeParamsCount == 0) {
      throw new IllegalArgumentException(
          String.format(
              "Cannot parameterize `%s` because it does not have any type parameters.",
              rawType.getTypeName()));
    }
    if (typeArguments.length != typeParamsCount) {
      throw new IllegalArgumentException(
          String.format(
              "Expected %d type arguments for `%s`, but got %d.",
              typeParamsCount, rawType.getTypeName(), typeArguments.length));
    }
    for (Type arg : typeArguments) {
      if (arg instanceof Class<?> clazz) {
        if (clazz.isPrimitive()) {
          throw new IllegalArgumentException(
              String.format(
                  "`%s.class` is not a valid type argument. Did you mean `%s.class`?",
                  clazz, Reflection.toWrapperType(clazz).getSimpleName()));
        }
      }
    }
    try {
      return (ParameterizedType) TypeFactory.parameterizedClass(rawType, typeArguments);
    } catch (TypeArgumentNotInBoundException e) {
      throw new IllegalArgumentException(
          String.format(
              "Type argument `%s` for type parameter `%s` is not within bound `%s`.",
              e.getArgument().getTypeName(),
              e.getParameter().getTypeName(),
              e.getBound().getTypeName()));
    }
  }
@Test
  public void createParameterizedTypeWithWrongNumberOfTypeArguments() {
    var t =
        catchThrowable(
            () -> Types.parameterizedType(Map.class, Integer.class, String.class, URL.class));

    assertThat(t)
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessage("Expected 2 type arguments for `java.util.Map`, but got 3.");
  }