You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Incorrect type narrowing after Array.isArray typeguard on union with generic #46534
function logStringAssumption(input: string) {
console.log(`Type system is sure that typeof ${input} (aka '${typeof input}') must be 'string'`);
}
function test<I>(input: string[] | I) {
if (Array.isArray(input)) {
logStringAssumption(input[0]);
}
}
test([12345]);
🙁 Actual behavior
In line 6 of the code above (line 8 in the screenshot), input is inferred to be of type string[]
Passing input[0] into logStringAssumption compiles successfully
🙂 Expected behavior
The typechecker should account for the fact, that I can be some other array type such as number[]
In line 6, input should be inferred to be of type string[] | (I & any[])
Passing input[0] should not compile as any is not assignable to string
functionisNullish<T>(x: T|null|undefined): x is T{returnx!=null;}
We don’t have negated types, so the closest you can get for now is taking the union of a generic with a concrete type and letting type inference exclude the concrete case.
The idea is the same - by accepting string[] | I as a parameter, the type inference machinery ensures I isn't instantiated with string[]. It's not perfect (you can instantiate the generic manually, e.g.), but it's the closest we can get without negated types.
Activity
k-tten commentedon Oct 26, 2021
You haven't defined the typeIand thus TypeScript has no reason to assume thatIis an array-like type?Ok nevermind I see the generic 😅
I don't quite agree that
inputshould be inferred asstring[] | (I & any[])thoI see where you're coming from, but I think this problem arises from design: why would a function accept
string[]or any type (the generic)fatcerberus commentedon Oct 26, 2021
Usually for a same reason you might write:
We don’t have negated types, so the closest you can get for now is taking the union of a generic with a concrete type and letting type inference exclude the concrete case.
k-tten commentedon Oct 26, 2021
Eh I don't see the similarity in your example and theirs. Would you mind sharing another example? 🥲
I see some similarities but I think the type guard and
null | undefinedmakes it a tad different.fatcerberus commentedon Oct 26, 2021
The idea is the same - by accepting
string[] | Ias a parameter, the type inference machinery ensuresIisn't instantiated withstring[]. It's not perfect (you can instantiate the generic manually, e.g.), but it's the closest we can get without negated types.andrewbranch commentedon Nov 4, 2021
Duplicate of #45686
typescript-bot commentedon Nov 7, 2021
This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.