Description
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.