@PublicEvolving
    public static <IN1, IN2, OUT> TypeInformation<OUT> getJoinReturnTypes(
            JoinFunction<IN1, IN2, OUT> joinInterface,
            TypeInformation<IN1> in1Type,
            TypeInformation<IN2> in2Type) {
        return getJoinReturnTypes(joinInterface, in1Type, in2Type, null, false);
    }
@Test
    void testInputInferenceWithCustomTupleAndRichFunction() {
        JoinFunction<
                        CustomTuple2WithArray<Long>,
                        CustomTuple2WithArray<Long>,
                        CustomTuple2WithArray<Long>>
                function = new JoinWithCustomTuple2WithArray<>();

        TypeInformation<?> ti =
                TypeExtractor.getJoinReturnTypes(
                        function,
                        new TypeHint<CustomTuple2WithArray<Long>>() {}.getTypeInfo(),
                        new TypeHint<CustomTuple2WithArray<Long>>() {}.getTypeInfo());

        assertThat(ti.isTupleType()).isTrue();
        TupleTypeInfo<?> tti = (TupleTypeInfo<?>) ti;
        assertThat(tti.getTypeAt(1)).isEqualTo(BasicTypeInfo.LONG_TYPE_INFO);

        assertThat(tti.getTypeAt(0) instanceof ObjectArrayTypeInfo<?, ?>).isTrue();
        ObjectArrayTypeInfo<?, ?> oati = (ObjectArrayTypeInfo<?, ?>) tti.getTypeAt(0);
        assertThat(oati.getComponentInfo()).isEqualTo(BasicTypeInfo.LONG_TYPE_INFO);
    }