-
Notifications
You must be signed in to change notification settings - Fork 12.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve narrowing of generic types in control flow analysis (#43183)
* Narrow type variables with union constraints when merited by contextual type * Narrow generics with union type constraints as indicated by contextual type * Accept new baselines * Add tests * Fix circularity for JSX elements * Remove unnecessary isConstraintPosition information from flow cache key * Update comment * Add additional tests * Rename to getNarrowableTypeForReference, remove getConstraintForLocation * Add comment * Fix removal of undefined in destructurings with initializers * Use getContextFreeTypeOfExpression in discriminateContextualTypeByObjectMembers * In obj[x], use constraint of obj's type only when x's type is non-generic * Add comment
- Loading branch information
1 parent
8a5939f
commit 15fae38
Showing
30 changed files
with
1,400 additions
and
158 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
152 changes: 152 additions & 0 deletions
152
tests/baselines/reference/controlFlowGenericTypes.errors.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(49,15): error TS2345: Argument of type 'undefined' is not assignable to parameter of type 'Box<T>'. | ||
tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(55,15): error TS2345: Argument of type 'undefined' is not assignable to parameter of type 'Box<unknown>'. | ||
tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(81,11): error TS2339: Property 'foo' does not exist on type 'MyUnion'. | ||
Property 'foo' does not exist on type 'AA'. | ||
tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(90,44): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value. | ||
tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(91,11): error TS2339: Property 'foo' does not exist on type 'MyUnion'. | ||
Property 'foo' does not exist on type 'AA'. | ||
|
||
|
||
==== tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts (5 errors) ==== | ||
function f1<T extends string | undefined>(x: T, y: { a: T }, z: [T]): string { | ||
if (x) { | ||
x; | ||
x.length; | ||
return x; | ||
} | ||
if (y.a) { | ||
y.a.length; | ||
return y.a; | ||
} | ||
if (z[0]) { | ||
z[0].length; | ||
return z[0]; | ||
} | ||
return "hello"; | ||
} | ||
|
||
function f2<T>(x: Extract<T, string | undefined> | null): string { | ||
if (x) { | ||
x; | ||
x.length; | ||
return x; | ||
} | ||
return "hello"; | ||
} | ||
|
||
interface Box<T> { | ||
item: T; | ||
} | ||
|
||
declare function isBox(x: any): x is Box<unknown>; | ||
declare function isUndefined(x: unknown): x is undefined; | ||
declare function unbox<T>(x: Box<T>): T; | ||
|
||
function g1<T extends Box<T> | undefined>(x: T) { | ||
if (isBox(x)) { | ||
unbox(x); | ||
} | ||
} | ||
|
||
function g2<T extends Box<T> | undefined>(x: T) { | ||
if (!isUndefined(x)) { | ||
unbox(x); | ||
} | ||
} | ||
|
||
function g3<T extends Box<T> | undefined>(x: T) { | ||
if (!isBox(x)) { | ||
unbox(x); // Error | ||
~ | ||
!!! error TS2345: Argument of type 'undefined' is not assignable to parameter of type 'Box<T>'. | ||
} | ||
} | ||
|
||
function g4<T extends Box<T> | undefined>(x: T) { | ||
if (isUndefined(x)) { | ||
unbox(x); // Error | ||
~ | ||
!!! error TS2345: Argument of type 'undefined' is not assignable to parameter of type 'Box<unknown>'. | ||
} | ||
} | ||
|
||
// Repro from #13995 | ||
|
||
declare function takeA(val: 'A'): void; | ||
export function bounceAndTakeIfA<AB extends 'A' | 'B'>(value: AB): AB { | ||
if (value === 'A') { | ||
takeA(value); | ||
return value; | ||
} | ||
else { | ||
return value; | ||
} | ||
} | ||
|
||
// Repro from #13995 | ||
|
||
type Common = { id: number }; | ||
type AA = { tag: 'A', id: number }; | ||
type BB = { tag: 'B', id: number, foo: number }; | ||
|
||
type MyUnion = AA | BB; | ||
|
||
const fn = (value: MyUnion) => { | ||
value.foo; // Error | ||
~~~ | ||
!!! error TS2339: Property 'foo' does not exist on type 'MyUnion'. | ||
!!! error TS2339: Property 'foo' does not exist on type 'AA'. | ||
if ('foo' in value) { | ||
value.foo; | ||
} | ||
if (value.tag === 'B') { | ||
value.foo; | ||
} | ||
}; | ||
|
||
const fn2 = <T extends MyUnion>(value: T): MyUnion => { | ||
~~~~~~~ | ||
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value. | ||
value.foo; // Error | ||
~~~ | ||
!!! error TS2339: Property 'foo' does not exist on type 'MyUnion'. | ||
!!! error TS2339: Property 'foo' does not exist on type 'AA'. | ||
if ('foo' in value) { | ||
value.foo; | ||
} | ||
if (value.tag === 'B') { | ||
value.foo; | ||
} | ||
}; | ||
|
||
// Repro from #13995 | ||
|
||
type A1 = { | ||
testable: true | ||
doTest: () => void | ||
} | ||
type B1 = { | ||
testable: false | ||
}; | ||
|
||
type Union = A1 | B1 | ||
|
||
function notWorking<T extends Union>(object: T) { | ||
if (!object.testable) return; | ||
object.doTest(); | ||
} | ||
|
||
// Repro from #42939 | ||
|
||
interface A { | ||
a: number | null; | ||
}; | ||
|
||
function get<K extends keyof A>(key: K, obj: A): number { | ||
const value = obj[key]; | ||
if (value !== null) { | ||
return value; | ||
} | ||
return 0; | ||
}; | ||
|
Oops, something went wrong.