Closed
Description
TypeScript Version: 3.7.2
Search Terms: Optional Chaining, Generic type, type information lost
While playing around on my side project, I found an edge case where optional chaining would fail to infer the correct types, while everything works fine without it.
Expected behavior: Line 30 yields no type error. Optional chaining narrows generic parameter down correctly and infers type {hello: "there", general: "kenobi}
as in Line 23.
Actual behavior: TS yields error on line 29 and 30, data not in type 'A'. it seems like any type information of the generic type is lost.
Code
interface Foo { }
interface Bar { }
interface FooBar<A> { data: A}
type FooBarFactory<A> = A extends FooBar<infer T> ? {
apply(foo: Foo, arg: T): BarFactory<A> | undefined
} : never;
interface BarFactory<A> {
apply(bar: Bar): A,
}
type DomainFooBar = FooBar<{ hello: "there", general: "kenobi" }> | FooBar<{chuck: "norris"}>
function test(declaration: FooBarFactory<DomainFooBar>) {
const f1 = declaration.apply({},{hello: "there", general: "kenobi", chuck: "norris"});
if (!f1) return;
const f2 = f1.apply({});
if (!f2) return;
if ("hello" in f2.data) {
f2.data.general
}
const fooBar = declaration.apply({},{hello: "there", general: "kenobi", chuck: "norris"})?.apply({});
if (!fooBar) return;
if ("hello" in fooBar.data) {
fooBar.data.general
}
}
Output
"use strict";
function test(declaration) {
var _a;
const f1 = declaration.apply({}, { hello: "there", general: "kenobi", chuck: "norris" });
if (!f1)
return;
const f2 = f1.apply({});
if (!f2)
return;
if ("hello" in f2.data) {
f2.data.general;
}
const fooBar = (_a = declaration.apply({}, { hello: "there", general: "kenobi", chuck: "norris" })) === null || _a === void 0 ? void 0 : _a.apply({});
if (!fooBar)
return;
if ("hello" in fooBar.data) {
fooBar.data.general;
}
}
Compiler Options
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"useDefineForClassFields": false,
"alwaysStrict": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"downlevelIteration": false,
"noEmitHelpers": false,
"noLib": false,
"noStrictGenericChecks": false,
"noUnusedLocals": false,
"noUnusedParameters": false,
"esModuleInterop": true,
"preserveConstEnums": false,
"removeComments": false,
"skipLibCheck": false,
"checkJs": false,
"allowJs": false,
"declaration": true,
"experimentalDecorators": false,
"emitDecoratorMetadata": false,
"target": "ES2017",
"module": "ESNext"
}
}
Playground Link: Provided