Reputation: 983
I could see that TS can't recognize the type of a param if it's derived from another by using ternary and extends.
Simplifying what I'm trying to achieve:
enum Types {
A = "a",
B = "b",
}
type A = { a: string };
type B = {};
function fn<T extends Types.A | Types.B>(
first: T,
second: T extends Types.A ? A : B
) {
if (first === Types.A) {
second.a;
// Property 'a' does not exist on type 'A | B'.
// Property 'a' does not exist on type 'B'.ts(2339)
}
}
TS should have recognized that second
has type A
inside the if
block. Since first
is Types.A
, second
has type A
, accordingly to the parameter type declared at the function signature.
I thought that the extends
could be the problem here but I could check that TS can recognize the derived type correctly in the following case:
function fnWithMultipleTypes<T extends Types.A | Types.B>(arg: T) {}
function fn<T extends Types.A>(arg: T) {
fnWithMultipleTypes(arg)
}
The arg
from fn
matches the type T
from fnWithMultipleTypes
.
Am I missing something?
Upvotes: 1
Views: 280
Reputation: 250336
The main problem is that first === Types.A
narrows first
not the type parameter T
. In fact there is no way to narrow a type parameter.
What you could do is use a union of tuple types, and destructure the parameters in the parameter list. In newer versions of TypeScript the compiler will be able to follow that the destructured parameters are related:
enum Types {
A = "a",
B = "b",
}
type A = { a: string };
type B = {};
function fn(
...[first, second]:
| [first: Types.A, second: A]
| [first: Types.B, second: B]
) {
if (first === Types.A) {
second.a;
}
}
Upvotes: 1