@PublicEvolving
    public static <IN, OUT> TypeInformation<OUT> getMapReturnTypes(
            MapFunction<IN, OUT> mapInterface, TypeInformation<IN> inType) {
        return getMapReturnTypes(mapInterface, inType, null, false);
    }
@SuppressWarnings({"unchecked", "rawtypes"})
    @Test
    void testMultiDimensionalArray() {
        // tuple array
        MapFunction<?, ?> function =
                new MapFunction<Tuple2<Integer, Double>[][], Tuple2<Integer, Double>[][]>() {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public Tuple2<Integer, Double>[][] map(Tuple2<Integer, Double>[][] value)
                            throws Exception {
                        return null;
                    }
                };
        TypeInformation<?> ti =
                TypeExtractor.getMapReturnTypes(
                        (MapFunction) function,
                        TypeInformation.of(new TypeHint<Tuple2<Integer, Double>[][]>() {}));
        assertThat(ti.toString())
                .isEqualTo(
                        "ObjectArrayTypeInfo<ObjectArrayTypeInfo<Java Tuple2<Integer, Double>>>");

        // primitive array
        function =
                new MapFunction<int[][][], int[][][]>() {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public int[][][] map(int[][][] value) throws Exception {
                        return null;
                    }
                };
        ti =
                TypeExtractor.getMapReturnTypes(
                        (MapFunction) function, TypeInformation.of(new TypeHint<int[][][]>() {}));
        assertThat(ti).hasToString("ObjectArrayTypeInfo<ObjectArrayTypeInfo<int[]>>");

        // basic array
        function =
                new MapFunction<Integer[][][], Integer[][][]>() {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public Integer[][][] map(Integer[][][] value) throws Exception {
                        return null;
                    }
                };
        ti =
                TypeExtractor.getMapReturnTypes(
                        (MapFunction) function,
                        TypeInformation.of(new TypeHint<Integer[][][]>() {}));
        assertThat(ti.toString())
                .isEqualTo("ObjectArrayTypeInfo<ObjectArrayTypeInfo<BasicArrayTypeInfo<Integer>>>");

        // pojo array
        function =
                new MapFunction<CustomType[][][], CustomType[][][]>() {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public CustomType[][][] map(CustomType[][][] value) throws Exception {
                        return null;
                    }
                };
        ti =
                TypeExtractor.getMapReturnTypes(
                        (MapFunction) function,
                        TypeInformation.of(new TypeHint<CustomType[][][]>() {}));

        assertThat(ti.toString())
                .isEqualTo(
                        "ObjectArrayTypeInfo<ObjectArrayTypeInfo<ObjectArrayTypeInfo<"
                                + "PojoType<org.apache.flink.api.java.typeutils.TypeExtractorTest$CustomType, fields = [myField1: String, myField2: Integer]>"
                                + ">>>");

        // generic array
        ti =
                TypeExtractor.getMapReturnTypes(
                        (MapFunction) new MapperWithMultiDimGenericArray<String>(),
                        TypeInformation.of(new TypeHint<String[][][]>() {}));
        assertThat(ti.toString())
                .isEqualTo(
                        "ObjectArrayTypeInfo<ObjectArrayTypeInfo<ObjectArrayTypeInfo<Java Tuple1<String>>>>");
    }