Closed
Description
Bug Report
🔎 Search Terms
CFA generic function
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about CFA and type inference
⏯ Playground Link
Playground link with relevant code
💻 Code
interface RouteParams {
'/user': { userId: string },
'/product': { productId: string },
'/list': { pageSize: number, current: number },
}
function navigateTo<T extends keyof RouteParams>(url: T, params: RouteParams[T]) {
// NOT WORK HERE !!!
if (url === '/user') {
console.log(params.userId)
}
}
// WORKS WELL
navigateTo('/user', { userId: '123' });
// @ts-expect-error
navigateTo('/user', { xxxxx: '123' });
🙁 Actual behavior
Property 'userId' does not exist on type '{ userId: string; } | { productId: string; } | { pageSize: number; current: number; }'.
Property 'userId' does not exist on type '{ productId: string; }'.(2339)
CFA works well outside the function call, but not works inside the function.
🙂 Expected behavior
Compile successfully.
Metadata
Metadata
Assignees
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
MartinJohns commentedon Aug 6, 2022
The error is correct. You wrongly assume that
T
will always only be one specific key, but this is valid code:You want #27808.
k8w commentedon Aug 6, 2022
@MartinJohns
Actually,
T
here is not'/user' | '/product'
, because it is infered as'/user'
:MartinJohns commentedon Aug 6, 2022
It's allowed to explicitly specify the type parameter, as I did. In that case the type parameter is not inferred anymore.
But even if you let it infer the type you can have the same issue:
Within the implementation of the function the compiler can't know if the type represents only one key, or multiple. So it errs on the safe side.
k8w commentedon Aug 6, 2022
T
maybe multiple keys indeed, but when it checked byurl === '/user'
, shouldn't CFA narrowed the T to single key?For example, this works well:
So why it doesn't works inside the function ?
MartinJohns commentedon Aug 6, 2022
No. You're only narrowing the type of the specific variable.
T
can be"/user" | "/product"
, andurl
is"/user"
, butparams
isRouteParams["/product"]
. This is perfectly legit to call, as I demonstrated twice.k8w commentedon Aug 6, 2022
Actually
url
is not narrowed at all, see this:whzx5byb commentedon Aug 6, 2022
@k8w I'm not sure why
v
is still marked asT extends 'a' | 'b'
but it is narrowed in fact. See the examples in #43183.fatcerberus commentedon Aug 6, 2022
Union types strike again! This recent comment of mine #50145 (comment) comes to mind, specifically the last part:
typescript-bot commentedon Aug 10, 2022
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.