From 20826cac5a91c42c74f800b9f289b5b279b15a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Fri, 30 Sep 2022 20:57:21 +0200 Subject: [PATCH 1/2] Allow narrowing `{}` originating in `unknown` using `instanceof` --- src/compiler/checker.ts | 4 +++- .../reference/inKeywordAndUnknown.errors.txt | 6 ++++++ .../reference/inKeywordAndUnknown.js | 10 ++++++++++ .../reference/inKeywordAndUnknown.symbols | 13 ++++++++++++ .../reference/inKeywordAndUnknown.types | 20 +++++++++++++++++++ tests/cases/compiler/inKeywordAndUnknown.ts | 6 ++++++ 6 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0e4f940238cd3..566c26b6ace67 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25721,7 +25721,9 @@ namespace ts { const nonConstructorTypeInUnion = find((rightType as UnionType).types, (t) => !isConstructorType(t)); if (!nonConstructorTypeInUnion) return type; } - + if (assumeTrue && declaredType === unknownType && isEmptyAnonymousObjectType(type)) { + return targetType; + } return getNarrowedType(type, targetType, assumeTrue, /*checkDerived*/ true); } diff --git a/tests/baselines/reference/inKeywordAndUnknown.errors.txt b/tests/baselines/reference/inKeywordAndUnknown.errors.txt index 61c2cd2a2a364..79a2f6fa09324 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.errors.txt +++ b/tests/baselines/reference/inKeywordAndUnknown.errors.txt @@ -20,4 +20,10 @@ tests/cases/compiler/inKeywordAndUnknown.ts(12,18): error TS2638: Type '{}' may } y; // {} } + + + // repro #51007 + function isHTMLTable(table: unknown): boolean { + return !!table && table instanceof Object && 'html' in table; + } \ No newline at end of file diff --git a/tests/baselines/reference/inKeywordAndUnknown.js b/tests/baselines/reference/inKeywordAndUnknown.js index 354bb8c4b1884..e5ea7bcf1e5a8 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.js +++ b/tests/baselines/reference/inKeywordAndUnknown.js @@ -15,6 +15,12 @@ function f(x: {}, y: unknown) { } y; // {} } + + +// repro #51007 +function isHTMLTable(table: unknown): boolean { + return !!table && table instanceof Object && 'html' in table; +} //// [inKeywordAndUnknown.js] @@ -34,3 +40,7 @@ function f(x, y) { } y; // {} } +// repro #51007 +function isHTMLTable(table) { + return !!table && table instanceof Object && 'html' in table; +} diff --git a/tests/baselines/reference/inKeywordAndUnknown.symbols b/tests/baselines/reference/inKeywordAndUnknown.symbols index 145e92dc7f657..5ef77e54a270b 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.symbols +++ b/tests/baselines/reference/inKeywordAndUnknown.symbols @@ -31,3 +31,16 @@ function f(x: {}, y: unknown) { >y : Symbol(y, Decl(inKeywordAndUnknown.ts, 2, 17)) } + +// repro #51007 +function isHTMLTable(table: unknown): boolean { +>isHTMLTable : Symbol(isHTMLTable, Decl(inKeywordAndUnknown.ts, 15, 1)) +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 19, 21)) + + return !!table && table instanceof Object && 'html' in table; +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 19, 21)) +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 19, 21)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 19, 21)) +} + diff --git a/tests/baselines/reference/inKeywordAndUnknown.types b/tests/baselines/reference/inKeywordAndUnknown.types index 59a587598b23d..1a535ab48fdb5 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.types +++ b/tests/baselines/reference/inKeywordAndUnknown.types @@ -40,3 +40,23 @@ function f(x: {}, y: unknown) { >y : Record<"a", unknown> } + +// repro #51007 +function isHTMLTable(table: unknown): boolean { +>isHTMLTable : (table: unknown) => boolean +>table : unknown + + return !!table && table instanceof Object && 'html' in table; +>!!table && table instanceof Object && 'html' in table : boolean +>!!table && table instanceof Object : boolean +>!!table : boolean +>!table : boolean +>table : unknown +>table instanceof Object : boolean +>table : {} +>Object : ObjectConstructor +>'html' in table : boolean +>'html' : "html" +>table : Object +} + diff --git a/tests/cases/compiler/inKeywordAndUnknown.ts b/tests/cases/compiler/inKeywordAndUnknown.ts index 745dfbc1f9af5..a7f25f5529d81 100644 --- a/tests/cases/compiler/inKeywordAndUnknown.ts +++ b/tests/cases/compiler/inKeywordAndUnknown.ts @@ -16,3 +16,9 @@ function f(x: {}, y: unknown) { } y; // {} } + + +// repro #51007 +function isHTMLTable(table: unknown): boolean { + return !!table && table instanceof Object && 'html' in table; +} From 89872e656f5c1db4a8c8929dc2c508d03acc43d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Fri, 11 Nov 2022 22:26:15 +0100 Subject: [PATCH 2/2] Add extra test case --- .../reference/inKeywordAndUnknown.errors.txt | 7 ++++ .../reference/inKeywordAndUnknown.js | 13 ++++++++ .../reference/inKeywordAndUnknown.symbols | 23 +++++++++++++ .../reference/inKeywordAndUnknown.types | 32 +++++++++++++++++++ tests/cases/compiler/inKeywordAndUnknown.ts | 7 ++++ 5 files changed, 82 insertions(+) diff --git a/tests/baselines/reference/inKeywordAndUnknown.errors.txt b/tests/baselines/reference/inKeywordAndUnknown.errors.txt index 79a2f6fa09324..41dde018c8913 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.errors.txt +++ b/tests/baselines/reference/inKeywordAndUnknown.errors.txt @@ -26,4 +26,11 @@ tests/cases/compiler/inKeywordAndUnknown.ts(12,18): error TS2638: Type '{}' may function isHTMLTable(table: unknown): boolean { return !!table && table instanceof Object && 'html' in table; } + + function tryGetHtmlPropFromTable(table: unknown) { + if (!!table && table instanceof Object && 'html' in table && table.html instanceof Object) { + return table.html; + } + return undefined; + } \ No newline at end of file diff --git a/tests/baselines/reference/inKeywordAndUnknown.js b/tests/baselines/reference/inKeywordAndUnknown.js index e5ea7bcf1e5a8..9a25c77e6268c 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.js +++ b/tests/baselines/reference/inKeywordAndUnknown.js @@ -21,6 +21,13 @@ function f(x: {}, y: unknown) { function isHTMLTable(table: unknown): boolean { return !!table && table instanceof Object && 'html' in table; } + +function tryGetHtmlPropFromTable(table: unknown) { + if (!!table && table instanceof Object && 'html' in table && table.html instanceof Object) { + return table.html; + } + return undefined; +} //// [inKeywordAndUnknown.js] @@ -44,3 +51,9 @@ function f(x, y) { function isHTMLTable(table) { return !!table && table instanceof Object && 'html' in table; } +function tryGetHtmlPropFromTable(table) { + if (!!table && table instanceof Object && 'html' in table && table.html instanceof Object) { + return table.html; + } + return undefined; +} diff --git a/tests/baselines/reference/inKeywordAndUnknown.symbols b/tests/baselines/reference/inKeywordAndUnknown.symbols index 5ef77e54a270b..ddef5c201b058 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.symbols +++ b/tests/baselines/reference/inKeywordAndUnknown.symbols @@ -44,3 +44,26 @@ function isHTMLTable(table: unknown): boolean { >table : Symbol(table, Decl(inKeywordAndUnknown.ts, 19, 21)) } +function tryGetHtmlPropFromTable(table: unknown) { +>tryGetHtmlPropFromTable : Symbol(tryGetHtmlPropFromTable, Decl(inKeywordAndUnknown.ts, 21, 1)) +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 23, 33)) + + if (!!table && table instanceof Object && 'html' in table && table.html instanceof Object) { +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 23, 33)) +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 23, 33)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 23, 33)) +>table.html : Symbol(html) +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 23, 33)) +>html : Symbol(html) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + return table.html; +>table.html : Symbol(html) +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 23, 33)) +>html : Symbol(html) + } + return undefined; +>undefined : Symbol(undefined) +} + diff --git a/tests/baselines/reference/inKeywordAndUnknown.types b/tests/baselines/reference/inKeywordAndUnknown.types index 1a535ab48fdb5..a19888f7a9a3d 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.types +++ b/tests/baselines/reference/inKeywordAndUnknown.types @@ -60,3 +60,35 @@ function isHTMLTable(table: unknown): boolean { >table : Object } +function tryGetHtmlPropFromTable(table: unknown) { +>tryGetHtmlPropFromTable : (table: unknown) => Object | undefined +>table : unknown + + if (!!table && table instanceof Object && 'html' in table && table.html instanceof Object) { +>!!table && table instanceof Object && 'html' in table && table.html instanceof Object : boolean +>!!table && table instanceof Object && 'html' in table : boolean +>!!table && table instanceof Object : boolean +>!!table : boolean +>!table : boolean +>table : unknown +>table instanceof Object : boolean +>table : {} +>Object : ObjectConstructor +>'html' in table : boolean +>'html' : "html" +>table : Object +>table.html instanceof Object : boolean +>table.html : unknown +>table : Object & Record<"html", unknown> +>html : unknown +>Object : ObjectConstructor + + return table.html; +>table.html : Object +>table : Object & Record<"html", unknown> +>html : Object + } + return undefined; +>undefined : undefined +} + diff --git a/tests/cases/compiler/inKeywordAndUnknown.ts b/tests/cases/compiler/inKeywordAndUnknown.ts index a7f25f5529d81..cf27e39736894 100644 --- a/tests/cases/compiler/inKeywordAndUnknown.ts +++ b/tests/cases/compiler/inKeywordAndUnknown.ts @@ -22,3 +22,10 @@ function f(x: {}, y: unknown) { function isHTMLTable(table: unknown): boolean { return !!table && table instanceof Object && 'html' in table; } + +function tryGetHtmlPropFromTable(table: unknown) { + if (!!table && table instanceof Object && 'html' in table && table.html instanceof Object) { + return table.html; + } + return undefined; +}