Typescript: Type of generic function parameter not being inferred from interface generic type.

Created on 20 Mar 2019  Β·  2Comments  Β·  Source: microsoft/TypeScript


TypeScript Version: 3.4.0@rc


Search Terms:

Code

export type Prop<T> = { (): T }
export type PropType<T> = Prop<T>;
export type PropDefaultValue<T> = T;


export type PropValidatorFunction<T> = (value: T) => boolean;
export type PropValidator<T> = PropOptions<T>;


export type PropOptions<T> = {
    type: PropType<T>;

    value?: PropDefaultValue<T>,
    required?: boolean;
    validator?: PropValidatorFunction<T>;
}

export type RecordPropsDefinition<T> = {
    [K in keyof T]: PropValidator<T[K]>
}
export type PropsDefinition<T> = RecordPropsDefinition<T>;


declare function extend<T>({ props }: { props: PropsDefinition<T> }):  PropsDefinition<T>;

interface MyType {
    valid: boolean;
}

const r = extend({
    props: {
        // gets resolved to PropOptions<any> but should be PropType<MyType>
        notResolved: {
            type: Object as PropType<MyType>,

            // type not inferred 
            validator: (x) => {
                return x.valid;
            }
        },
        // gets resolved currectly to PropType<MyType>
        // on [email protected] doest resolve to PropType<MyType>
        explicit: {
            type: Object as PropType<MyType>,

            // type not inferred 
            validator: (x: MyType) => {
                return x.valid;
            }
        }
    }
})

// return type on 3.4@rc doesn't get resolved
r.explicit
r.notResolved
r.explicit.required
r.notResolved.required

Expected behavior:
The validator parameter as typescript type inferred from T.
Props record values to be resolved with the interface of PropType
Return type to be PropsDefinition<T>

Actual behavior:
Getting validator parameter resolved as any/unknown.
Record types are not getting resolve to the type PropOptions<T> in 3.4@rc but working in 3.3.x
The return type not working in 3.4@rc

Playground Link:
playground

Bug Fixed

Most helpful comment

The key issue here is that during the initial pass in type inference where we defer context sensitive function expressions and arrow functions, we never perform reverse mapped type inference on types that contain anything context sensitive. That is too conservative because, as illustrated by the examples, there are cases where we want to infer from the properties that aren't context sensitive and then apply those inferences to the context sensitive locations. I will be putting up a PR that does this.

All 2 comments

This hasn't worked particularly well in any version, but this seems to be regressing:

type Box<T> = {
    contents: T;
    contains?(content: T): boolean;
};
type Mapped<T> = {
    [K in keyof T]: Box<T[K]>;
}
declare function id<T>(arg: Mapped<T>): Mapped<T>;

// obj1: Mapped<{ foo: string }> βœ”
const obj1 = id({
    foo: {
        contents: ""
    }
});

// 3.3 obj2: Mapped<{ foo: any }> ❌
// 3.4 obj2: Mapped<{}> ❌❌
const obj2 = id({
    foo: {
        contents: "",
        // k: implicit any ❌
        contains(k) {
            return k.length > 0;
        }
    }
});

The key issue here is that during the initial pass in type inference where we defer context sensitive function expressions and arrow functions, we never perform reverse mapped type inference on types that contain anything context sensitive. That is too conservative because, as illustrated by the examples, there are cases where we want to infer from the properties that aren't context sensitive and then apply those inferences to the context sensitive locations. I will be putting up a PR that does this.

Was this page helpful?
0 / 5 - 0 ratings