This narrowing typeguard effect bleeds into subsequent statments on a type with bivariant type-parameter
See original GitHub issueTypeScript Version: 3.4.0-dev.201xxxxx (although 3.3 is also impacted) Search Terms: this type guards
If we create a type-guard that narrows this
on a type that has a type-parameter that is present only in a bivariant position, the effect of the type guard persists outside of the guarded block.
Code
type GetKnownKeys<G> = G extends GuardedMap<infer KnownKeys> ? KnownKeys: never;
interface GuardedMap<KnownKeys extends string> {
get(k: KnownKeys): number;
has<S extends string>(k: S): this is GuardedMap<S | GetKnownKeys<this>>;
}
declare let map: GuardedMap<never>;
map.get('bar') // err, as expected
if (map.has('foo')) {
map.get('foo').toExponential(); // ok as expected
if(map.has('bar'))
{
map.get('foo').toExponential(); // ok as expected
map.get('bar').toExponential(); // ok as expected
}
map.get('bar').toExponential(); /// OK!?!?! WHY ?!
}
map.get('bar') // OK ?!
Expected behavior:
Type guard only impacts the guarded block.
Actual behavior:
The effect of the type guard bleads into all subsequent statements. (marked with OK!?!?!
and OK?!
)
Note: With strictFunctionTypes
on, declaring get
as get: (k: KnownKeys) => number;
makes the code work as expected.
Playground Link: link
Related Issues: Similar to #14817
Found this while playing with a solution for #9619
Issue Analytics
- State:
- Created 5 years ago
- Reactions:1
- Comments:10 (7 by maintainers)
Top Results From Across the Web
Covariance and Contravariance in Generics - Microsoft Learn
A generic type parameter that is not marked covariant or contravariant is referred to as invariant. A brief summary of facts about variance...
Read more >c# compiler error 'Parameter must be input safe. Invalid ...
You declared T as covariant (using the out keyword) but you cannot take covariant parameters: (MSDN). In general, a covariant type parameter can...
Read more >The starting point for learning TypeScript
Find TypeScript starter projects: from Angular to React or Node.js and CLIs. ... Learn how to write declaration files to describe existing JavaScript....
Read more >Understanding Covariance and Contravariance of Generic ...
In this article, we will examine what covariant and contravariant generic types are. We will explain what it means for a generic type...
Read more >Kotlin generic variance modifiers | by Marcin Moskala
Kotlin is much safer than Java. Arrays in Kotlin have invariant type parameter. List interface has covariant type parameter, because it is immutable....
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Extremely good analysis @jack-williams
Here’s a simplified repro with fewer things going on
As for documentation, I have no idea how you would write this down in a way that anyone could find it unless they already knew what the doc said.
Sorry, I was assuming that
get
was written asget: (k: KnownKeys) => number;
. If it’s written as a method then you’re stuck with bivariance irrespective of strictness mode so there is no way to see the different behaviour.It’s probably not useful in this scenario, but across the board it’s generally useful and because it’s compositional it’s easier to implement. Where you see the benefit is when you have multiple branches, some of which return. In that case, gathering the flow types in a union is simple and effective. In your example, the problem only arises due to unsoundness in the type system.
It sort of feels like that, but maybe there is an easy fix. I think the problem is that any fix effectively amounts to a heuristic. I agree that documenting it, at the least, is worth the time.