Skip to content

Type inference issue when using generic function and generic constraint #24747

Closed
@ulrichb

Description

@ulrichb

TypeScript Version: 3.0.0-dev.20180607 and 2.9.1

Search Terms: type inference generic constraint function callback accessor

Code

type GenericFunc<TA, TB> = (x: TA) => TB;

class GenericFuncHolder<TA, TB> {
    constructor(readonly func: GenericFunc<TA, TB>) { }
}

function wrap<TA, TB>(func: GenericFunc<TA, TB>) { return new GenericFuncHolder(func); }

class X { readonly xProperty = "xProperty"; }

class Y<TX> { readonly x: TX | undefined; readonly yProperty = "yProperty"; }
class YDerived<TX> extends Y<TX> { readonly yDerivedProperty = "yDerivedProperty"; }

class GenericTarget<TX, TY> {

    constructor(readonly x: TX, readonly y: TY) { }

    holderParams<TX2, TY2 extends Y<TX2>>(
        holderX: GenericFuncHolder<TX, TX2>, holderY: GenericFuncHolder<TY, TY2>,
        use: (x: TX2, y: TY2) => void,
    ) {
        use(holderX.func(this.x), holderY.func(this.y));
    }

    funcParamsWithoutConstraint<TX2, TY2>(
        funcX: GenericFunc<TX, TX2>, funcY: GenericFunc<TY, TY2>,
        use: (x: TX2, y: TY2) => void,
    ) {
        use(wrap(funcX).func(this.x), wrap(funcY).func(this.y));
    }

    funcParamsWithConstraint_BAD<TX2, TY2 extends Y<TX2>>(
        funcX: GenericFunc<TX, TX2>, funcY: GenericFunc<TY, TY2>,
        use: (x: TX2, y: TY2) => void,
    ) {
        use(wrap(funcX).func(this.x), wrap(funcY).func(this.y));
    }
}


const genericClassTarget = new GenericTarget (new X(), new Y<X>());

// Type inference works when having the constraint but using `wrap()` on the call site:
genericClassTarget.holderParams(wrap(x => new X()), wrap(y => new YDerived<X>()),
    (x2, y2) => console.log(x2.xProperty, y2.yDerivedProperty));

// Type inference works without the `TY2` constraint:
genericClassTarget.funcParamsWithoutConstraint(x => new X(), y => new YDerived<X>(),
    (x2, y2) => console.log(x2.xProperty, y2.yDerivedProperty));

// Type inference doesn't work, TSC emits "TS2339: Property 'xProperty' does not exist on type '{}'"
genericClassTarget.funcParamsWithConstraint_BAD(x => new X(), y => new YDerived<X>(),
    (x2, y2) => console.log(x2.xProperty, y2.yDerivedProperty));

(Sorry for the long repro, it's the simplified version of a production issue and the inference issue seems to be a very special case.)

Expected behavior:
Type inference works for funcParamsWithConstraint_BAD as it works for holderParams and funcParamsWithoutConstraint. It seems that the generic constraint of TY2 narrows TX2 to {}, but this works when using wrap() on the call site. Which is the reason, why I would expect funcParamsWithConstraint_BAD to work just fine.

Actual behavior:
"TS2339: Property 'xProperty' does not exist on type '{}'" in the last line.

Playground Link: Playground Link

EDIT: Extended repro to make it more realistic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixedRescheduledThis issue was previously scheduled to an earlier milestone

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions