@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 testNestedTupleGenerics() {
        RichMapFunction<?, ?> function =
                new RichMapFunction<Nested<String, Integer>, Nested<String, Integer>>() {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public Nested<String, Integer> map(Nested<String, Integer> value)
                            throws Exception {
                        return null;
                    }
                };

        TypeInformation<?> ti =
                TypeExtractor.getMapReturnTypes(
                        function,
                        (TypeInformation)
                                TypeInformation.of(
                                        new TypeHint<
                                                Tuple2<String, Tuple2<Integer, Integer>>>() {}));

        assertThat(ti.isTupleType()).isTrue();
        assertThat(ti.getArity()).isEqualTo(2);

        TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti;
        assertThat(tti.getTypeClass()).isEqualTo(Nested.class);

        assertThat(tti.getTypeAt(0)).isEqualTo(BasicTypeInfo.STRING_TYPE_INFO);
        assertThat(tti.getTypeAt(1).isTupleType()).isTrue();
        assertThat(tti.getTypeAt(1).getArity()).isEqualTo(2);

        // Nested
        TupleTypeInfo<?> tti2 = (TupleTypeInfo<?>) tti.getTypeAt(1);
        assertThat(tti2.getTypeClass()).isEqualTo(Tuple2.class);
        assertThat(tti2.getTypeAt(0)).isEqualTo(BasicTypeInfo.INT_TYPE_INFO);
        assertThat(tti2.getTypeAt(1)).isEqualTo(BasicTypeInfo.INT_TYPE_INFO);
    }