TypeScript: Generic index access on target type and return based on discriminated types do not work.
TypeScript Version: ^3.5.0
Search Terms: index access target type discriminated types
Code
interface TypeA {a: string; }
interface TypeB {b: string; }
class A implements TypeA {public a: string = ""; }
class B implements TypeB {public b: string = ""; }
enum Types {
A= "a", B = "b",
}
interface TypeInterfaces {
[Types.A]: TypeA;
[Types.B]: TypeB;
}
function create<T extends Types>(type: T): TypeInterfaces[T] {
if (type === Types.A) {
return new A;
} else if (type === Types.B) {
return new B;
}
throw new Error();
}
Expected behavior: The compiler knows with discriminated types exactly what it needs to return and there are no errors as in 3.4 or lower.
Actual behavior: The compiler gives an error that we cannot assign the returned A or B to TypeA & TypeB regardless of the fact that we actually know for sure with discriminated types that we need to return either TypeA or TypeB.
Playground Link: Try it on playground
Related Issues: This issue was specifically introduced here for v3.5 https://github.com/microsoft/TypeScript/pull/30769 by @ahejlsberg when it was decided to have the target type on index types as intersections.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 22 (3 by maintainers)
@fatcerberus Even a lower bound would not be sufficient I think. A truthy type test of
typeof x === "string"does not rule outT = "some string literal", so a lower bound ofstringwould be incorrect. I don’t think there is anyway to get the right behaviour with bounding, and you really need a new predicate like: #25879, #27808, #28430.TS doesn’t have any kind of exhaustiveness checking AFAIK so if it were a union instead of an intersection, nothing would stop you from returning the wrong thing in an else-clause, for example. Prior to TS 3.5 (when it was a union) you could just replace your entire function with
return new A;and it would typecheck fine (try it in the playground!). And then be wrong at the call site that asked for aB.Trust me—to make this work you need some way to narrow
T, which isn’t currently possible. 😢@archfz Right, that’s why I said it gets more complicated with generics - you can’t narrow a type parameter. I was merely explaining where the intersection was originating from.
This approach will not scale once the constructors need arguments that must be supplied to
create.