Properly handle partial union type properties in isTypePresencePossible (#53794)

This commit is contained in:
Anders Hejlsberg 2023-04-16 17:24:10 -07:00 committed by GitHub
parent 378ffa4bc9
commit e782cef221
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 456 additions and 1 deletions

View File

@ -27006,7 +27006,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function isTypePresencePossible(type: Type, propName: __String, assumeTrue: boolean) {
const prop = getPropertyOfType(type, propName);
return prop ?
!!(prop.flags & SymbolFlags.Optional) || assumeTrue :
!!(prop.flags & SymbolFlags.Optional || getCheckFlags(prop) & CheckFlags.Partial) || assumeTrue :
!!getApplicableIndexInfoForName(type, propName) || !assumeTrue;
}

View File

@ -432,4 +432,33 @@ tests/cases/compiler/inKeywordTypeguard.ts(186,21): error TS2322: Type 'T' is no
const f = <P extends object>(a: P & {}) => {
"foo" in a;
};
// Repro from #53773
function test1<T extends any[] | Record<string, any>>(obj: T) {
if (Array.isArray(obj) || 'length' in obj) {
obj; // T
}
else {
obj; // T
}
}
function test2<T extends any[] | Record<string, any>>(obj: T) {
if (Array.isArray(obj)) {
obj; // T & any[]
}
else {
obj; // T
}
}
function test3<T extends any[] | Record<string, any>>(obj: T) {
if ('length' in obj) {
obj; // T
}
else {
obj; // T
}
}

View File

@ -353,6 +353,35 @@ function isHTMLTable<T extends object | null>(table: T): boolean {
const f = <P extends object>(a: P & {}) => {
"foo" in a;
};
// Repro from #53773
function test1<T extends any[] | Record<string, any>>(obj: T) {
if (Array.isArray(obj) || 'length' in obj) {
obj; // T
}
else {
obj; // T
}
}
function test2<T extends any[] | Record<string, any>>(obj: T) {
if (Array.isArray(obj)) {
obj; // T & any[]
}
else {
obj; // T
}
}
function test3<T extends any[] | Record<string, any>>(obj: T) {
if ('length' in obj) {
obj; // T
}
else {
obj; // T
}
}
//// [inKeywordTypeguard.js]
@ -675,3 +704,28 @@ function isHTMLTable(table) {
const f = (a) => {
"foo" in a;
};
// Repro from #53773
function test1(obj) {
if (Array.isArray(obj) || 'length' in obj) {
obj; // T
}
else {
obj; // T
}
}
function test2(obj) {
if (Array.isArray(obj)) {
obj; // T & any[]
}
else {
obj; // T
}
}
function test3(obj) {
if ('length' in obj) {
obj; // T
}
else {
obj; // T
}
}

View File

@ -879,3 +879,69 @@ const f = <P extends object>(a: P & {}) => {
};
// Repro from #53773
function test1<T extends any[] | Record<string, any>>(obj: T) {
>test1 : Symbol(test1, Decl(inKeywordTypeguard.ts, 353, 2))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 357, 15))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 357, 54))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 357, 15))
if (Array.isArray(obj) || 'length' in obj) {
>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 357, 54))
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 357, 54))
obj; // T
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 357, 54))
}
else {
obj; // T
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 357, 54))
}
}
function test2<T extends any[] | Record<string, any>>(obj: T) {
>test2 : Symbol(test2, Decl(inKeywordTypeguard.ts, 364, 1))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 366, 15))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 366, 54))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 366, 15))
if (Array.isArray(obj)) {
>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 366, 54))
obj; // T & any[]
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 366, 54))
}
else {
obj; // T
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 366, 54))
}
}
function test3<T extends any[] | Record<string, any>>(obj: T) {
>test3 : Symbol(test3, Decl(inKeywordTypeguard.ts, 373, 1))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 375, 15))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 375, 54))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 375, 15))
if ('length' in obj) {
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 375, 54))
obj; // T
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 375, 54))
}
else {
obj; // T
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 375, 54))
}
}

View File

@ -1085,3 +1085,67 @@ const f = <P extends object>(a: P & {}) => {
};
// Repro from #53773
function test1<T extends any[] | Record<string, any>>(obj: T) {
>test1 : <T extends any[] | Record<string, any>>(obj: T) => void
>obj : T
if (Array.isArray(obj) || 'length' in obj) {
>Array.isArray(obj) || 'length' in obj : boolean
>Array.isArray(obj) : boolean
>Array.isArray : (arg: any) => arg is any[]
>Array : ArrayConstructor
>isArray : (arg: any) => arg is any[]
>obj : any[] | Record<string, any>
>'length' in obj : boolean
>'length' : "length"
>obj : T
obj; // T
>obj : T
}
else {
obj; // T
>obj : T
}
}
function test2<T extends any[] | Record<string, any>>(obj: T) {
>test2 : <T extends any[] | Record<string, any>>(obj: T) => void
>obj : T
if (Array.isArray(obj)) {
>Array.isArray(obj) : boolean
>Array.isArray : (arg: any) => arg is any[]
>Array : ArrayConstructor
>isArray : (arg: any) => arg is any[]
>obj : any[] | Record<string, any>
obj; // T & any[]
>obj : T & any[]
}
else {
obj; // T
>obj : T
}
}
function test3<T extends any[] | Record<string, any>>(obj: T) {
>test3 : <T extends any[] | Record<string, any>>(obj: T) => void
>obj : T
if ('length' in obj) {
>'length' in obj : boolean
>'length' : "length"
>obj : T
obj; // T
>obj : T
}
else {
obj; // T
>obj : T
}
}

View File

@ -452,4 +452,33 @@ tests/cases/compiler/inKeywordTypeguard.ts(186,21): error TS2638: Type 'NonNulla
const f = <P extends object>(a: P & {}) => {
"foo" in a;
};
// Repro from #53773
function test1<T extends any[] | Record<string, any>>(obj: T) {
if (Array.isArray(obj) || 'length' in obj) {
obj; // T
}
else {
obj; // T
}
}
function test2<T extends any[] | Record<string, any>>(obj: T) {
if (Array.isArray(obj)) {
obj; // T & any[]
}
else {
obj; // T
}
}
function test3<T extends any[] | Record<string, any>>(obj: T) {
if ('length' in obj) {
obj; // T
}
else {
obj; // T
}
}

View File

@ -353,6 +353,35 @@ function isHTMLTable<T extends object | null>(table: T): boolean {
const f = <P extends object>(a: P & {}) => {
"foo" in a;
};
// Repro from #53773
function test1<T extends any[] | Record<string, any>>(obj: T) {
if (Array.isArray(obj) || 'length' in obj) {
obj; // T
}
else {
obj; // T
}
}
function test2<T extends any[] | Record<string, any>>(obj: T) {
if (Array.isArray(obj)) {
obj; // T & any[]
}
else {
obj; // T
}
}
function test3<T extends any[] | Record<string, any>>(obj: T) {
if ('length' in obj) {
obj; // T
}
else {
obj; // T
}
}
//// [inKeywordTypeguard.js]
@ -676,3 +705,28 @@ function isHTMLTable(table) {
const f = (a) => {
"foo" in a;
};
// Repro from #53773
function test1(obj) {
if (Array.isArray(obj) || 'length' in obj) {
obj; // T
}
else {
obj; // T
}
}
function test2(obj) {
if (Array.isArray(obj)) {
obj; // T & any[]
}
else {
obj; // T
}
}
function test3(obj) {
if ('length' in obj) {
obj; // T
}
else {
obj; // T
}
}

View File

@ -879,3 +879,69 @@ const f = <P extends object>(a: P & {}) => {
};
// Repro from #53773
function test1<T extends any[] | Record<string, any>>(obj: T) {
>test1 : Symbol(test1, Decl(inKeywordTypeguard.ts, 353, 2))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 357, 15))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 357, 54))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 357, 15))
if (Array.isArray(obj) || 'length' in obj) {
>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 357, 54))
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 357, 54))
obj; // T
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 357, 54))
}
else {
obj; // T
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 357, 54))
}
}
function test2<T extends any[] | Record<string, any>>(obj: T) {
>test2 : Symbol(test2, Decl(inKeywordTypeguard.ts, 364, 1))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 366, 15))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 366, 54))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 366, 15))
if (Array.isArray(obj)) {
>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 366, 54))
obj; // T & any[]
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 366, 54))
}
else {
obj; // T
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 366, 54))
}
}
function test3<T extends any[] | Record<string, any>>(obj: T) {
>test3 : Symbol(test3, Decl(inKeywordTypeguard.ts, 373, 1))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 375, 15))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 375, 54))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 375, 15))
if ('length' in obj) {
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 375, 54))
obj; // T
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 375, 54))
}
else {
obj; // T
>obj : Symbol(obj, Decl(inKeywordTypeguard.ts, 375, 54))
}
}

View File

@ -1085,3 +1085,67 @@ const f = <P extends object>(a: P & {}) => {
};
// Repro from #53773
function test1<T extends any[] | Record<string, any>>(obj: T) {
>test1 : <T extends any[] | Record<string, any>>(obj: T) => void
>obj : T
if (Array.isArray(obj) || 'length' in obj) {
>Array.isArray(obj) || 'length' in obj : boolean
>Array.isArray(obj) : boolean
>Array.isArray : (arg: any) => arg is any[]
>Array : ArrayConstructor
>isArray : (arg: any) => arg is any[]
>obj : any[] | Record<string, any>
>'length' in obj : boolean
>'length' : "length"
>obj : T
obj; // T
>obj : T
}
else {
obj; // T
>obj : T
}
}
function test2<T extends any[] | Record<string, any>>(obj: T) {
>test2 : <T extends any[] | Record<string, any>>(obj: T) => void
>obj : T
if (Array.isArray(obj)) {
>Array.isArray(obj) : boolean
>Array.isArray : (arg: any) => arg is any[]
>Array : ArrayConstructor
>isArray : (arg: any) => arg is any[]
>obj : any[] | Record<string, any>
obj; // T & any[]
>obj : T & any[]
}
else {
obj; // T
>obj : T
}
}
function test3<T extends any[] | Record<string, any>>(obj: T) {
>test3 : <T extends any[] | Record<string, any>>(obj: T) => void
>obj : T
if ('length' in obj) {
>'length' in obj : boolean
>'length' : "length"
>obj : T
obj; // T
>obj : T
}
else {
obj; // T
>obj : T
}
}

View File

@ -355,3 +355,32 @@ function isHTMLTable<T extends object | null>(table: T): boolean {
const f = <P extends object>(a: P & {}) => {
"foo" in a;
};
// Repro from #53773
function test1<T extends any[] | Record<string, any>>(obj: T) {
if (Array.isArray(obj) || 'length' in obj) {
obj; // T
}
else {
obj; // T
}
}
function test2<T extends any[] | Record<string, any>>(obj: T) {
if (Array.isArray(obj)) {
obj; // T & any[]
}
else {
obj; // T
}
}
function test3<T extends any[] | Record<string, any>>(obj: T) {
if ('length' in obj) {
obj; // T
}
else {
obj; // T
}
}