From d1a6663c5f959c38694720080c5edcffc0f63df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 27 Jul 2022 11:54:18 +0200 Subject: [PATCH 1/2] Add an additional test for favoring the asserted type in type predicate narrowing --- ...orAssertedTypeThroughTypePredicate.symbols | 47 +++++++++++++++++ ...avorAssertedTypeThroughTypePredicate.types | 51 +++++++++++++++++++ ...owFavorAssertedTypeThroughTypePredicate.ts | 22 ++++++++ 3 files changed, 120 insertions(+) create mode 100644 tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.symbols create mode 100644 tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.types create mode 100644 tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts diff --git a/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.symbols b/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.symbols new file mode 100644 index 0000000000000..296bff21083aa --- /dev/null +++ b/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.symbols @@ -0,0 +1,47 @@ +=== tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts === +// repro 49988#issuecomment-1192016929 + +declare function isObject(value: unknown): value is Record; +>isObject : Symbol(isObject, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 0, 0)) +>value : Symbol(value, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 2, 26)) +>value : Symbol(value, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 2, 26)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +declare const obj1: {}; +>obj1 : Symbol(obj1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 4, 13)) + +obj1; // {} +>obj1 : Symbol(obj1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 4, 13)) + +if (isObject(obj1)) { +>isObject : Symbol(isObject, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 0, 0)) +>obj1 : Symbol(obj1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 4, 13)) + + obj1; // Record +>obj1 : Symbol(obj1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 4, 13)) + + obj1['attr']; +>obj1 : Symbol(obj1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 4, 13)) +} +obj1; // Record +>obj1 : Symbol(obj1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 4, 13)) + +declare const obj2: {} | undefined; +>obj2 : Symbol(obj2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 12, 13)) + +obj2; // {} | undefined +>obj2 : Symbol(obj2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 12, 13)) + +if (isObject(obj2)) { +>isObject : Symbol(isObject, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 0, 0)) +>obj2 : Symbol(obj2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 12, 13)) + + obj2; // Record +>obj2 : Symbol(obj2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 12, 13)) + + obj2['attr']; +>obj2 : Symbol(obj2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 12, 13)) +} +obj2; // Record | undefined +>obj2 : Symbol(obj2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 12, 13)) + diff --git a/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.types b/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.types new file mode 100644 index 0000000000000..a9902d995b991 --- /dev/null +++ b/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.types @@ -0,0 +1,51 @@ +=== tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts === +// repro 49988#issuecomment-1192016929 + +declare function isObject(value: unknown): value is Record; +>isObject : (value: unknown) => value is Record +>value : unknown + +declare const obj1: {}; +>obj1 : {} + +obj1; // {} +>obj1 : {} + +if (isObject(obj1)) { +>isObject(obj1) : boolean +>isObject : (value: unknown) => value is Record +>obj1 : {} + + obj1; // Record +>obj1 : Record + + obj1['attr']; +>obj1['attr'] : unknown +>obj1 : Record +>'attr' : "attr" +} +obj1; // Record +>obj1 : Record + +declare const obj2: {} | undefined; +>obj2 : {} | undefined + +obj2; // {} | undefined +>obj2 : {} | undefined + +if (isObject(obj2)) { +>isObject(obj2) : boolean +>isObject : (value: unknown) => value is Record +>obj2 : {} | undefined + + obj2; // Record +>obj2 : Record + + obj2['attr']; +>obj2['attr'] : unknown +>obj2 : Record +>'attr' : "attr" +} +obj2; // Record | undefined +>obj2 : Record | undefined + diff --git a/tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts b/tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts new file mode 100644 index 0000000000000..e43f19485daca --- /dev/null +++ b/tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts @@ -0,0 +1,22 @@ +// @noEmit: true +// @strict: true + +// repro 49988#issuecomment-1192016929 + +declare function isObject(value: unknown): value is Record; + +declare const obj1: {}; +obj1; // {} +if (isObject(obj1)) { + obj1; // Record + obj1['attr']; +} +obj1; // Record + +declare const obj2: {} | undefined; +obj2; // {} | undefined +if (isObject(obj2)) { + obj2; // Record + obj2['attr']; +} +obj2; // Record | undefined From 7610468c2b405594e6e947df2b4ef7928c078110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Fri, 29 Jul 2022 20:52:08 +0200 Subject: [PATCH 2/2] Add requested test cases --- ...ssertedTypeThroughTypePredicate.errors.txt | 51 +++++++++++++ ...orAssertedTypeThroughTypePredicate.symbols | 73 +++++++++++++----- ...avorAssertedTypeThroughTypePredicate.types | 76 ++++++++++++++----- ...owFavorAssertedTypeThroughTypePredicate.ts | 36 ++++++--- 4 files changed, 191 insertions(+), 45 deletions(-) create mode 100644 tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.errors.txt diff --git a/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.errors.txt b/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.errors.txt new file mode 100644 index 0000000000000..4ae99c4108d14 --- /dev/null +++ b/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.errors.txt @@ -0,0 +1,51 @@ +tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts(26,5): error TS7053: Element implicitly has an 'any' type because expression of type '"attr"' can't be used to index type '{}'. + Property 'attr' does not exist on type '{}'. +tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts(34,5): error TS7053: Element implicitly has an 'any' type because expression of type '"attr"' can't be used to index type '{}'. + Property 'attr' does not exist on type '{}'. + + +==== tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts (2 errors) ==== + // repro 49988#issuecomment-1192016929 + + declare function isObject1(value: unknown): value is Record; + + declare const obj1: {}; + if (isObject1(obj1)) { + obj1; + obj1['attr']; + } + // check type after conditional block + obj1; + + declare const obj2: {} | undefined; + if (isObject1(obj2)) { + obj2; + obj2['attr']; + } + // check type after conditional block + obj2; + + declare function isObject2(value: unknown): value is {}; + + declare const obj3: Record; + if (isObject2(obj3)) { + obj3; + obj3['attr']; + ~~~~~~~~~~~~ +!!! error TS7053: Element implicitly has an 'any' type because expression of type '"attr"' can't be used to index type '{}'. +!!! error TS7053: Property 'attr' does not exist on type '{}'. + } + // check type after conditional block + obj3; + + declare const obj4: Record | undefined; + if (isObject2(obj4)) { + obj4; + obj4['attr']; + ~~~~~~~~~~~~ +!!! error TS7053: Element implicitly has an 'any' type because expression of type '"attr"' can't be used to index type '{}'. +!!! error TS7053: Property 'attr' does not exist on type '{}'. + } + // check type after conditional block + obj4; + \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.symbols b/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.symbols index 296bff21083aa..5d7f64ae9f33f 100644 --- a/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.symbols +++ b/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.symbols @@ -1,47 +1,84 @@ === tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts === // repro 49988#issuecomment-1192016929 -declare function isObject(value: unknown): value is Record; ->isObject : Symbol(isObject, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 0, 0)) ->value : Symbol(value, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 2, 26)) ->value : Symbol(value, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 2, 26)) +declare function isObject1(value: unknown): value is Record; +>isObject1 : Symbol(isObject1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 0, 0)) +>value : Symbol(value, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 2, 27)) +>value : Symbol(value, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 2, 27)) >Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) declare const obj1: {}; >obj1 : Symbol(obj1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 4, 13)) -obj1; // {} +if (isObject1(obj1)) { +>isObject1 : Symbol(isObject1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 0, 0)) >obj1 : Symbol(obj1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 4, 13)) -if (isObject(obj1)) { ->isObject : Symbol(isObject, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 0, 0)) ->obj1 : Symbol(obj1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 4, 13)) - - obj1; // Record + obj1; >obj1 : Symbol(obj1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 4, 13)) obj1['attr']; >obj1 : Symbol(obj1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 4, 13)) } -obj1; // Record +// check type after conditional block +obj1; >obj1 : Symbol(obj1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 4, 13)) declare const obj2: {} | undefined; >obj2 : Symbol(obj2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 12, 13)) -obj2; // {} | undefined +if (isObject1(obj2)) { +>isObject1 : Symbol(isObject1, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 0, 0)) >obj2 : Symbol(obj2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 12, 13)) -if (isObject(obj2)) { ->isObject : Symbol(isObject, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 0, 0)) ->obj2 : Symbol(obj2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 12, 13)) - - obj2; // Record + obj2; >obj2 : Symbol(obj2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 12, 13)) obj2['attr']; >obj2 : Symbol(obj2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 12, 13)) } -obj2; // Record | undefined +// check type after conditional block +obj2; >obj2 : Symbol(obj2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 12, 13)) +declare function isObject2(value: unknown): value is {}; +>isObject2 : Symbol(isObject2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 18, 5)) +>value : Symbol(value, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 20, 27)) +>value : Symbol(value, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 20, 27)) + +declare const obj3: Record; +>obj3 : Symbol(obj3, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 22, 13)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +if (isObject2(obj3)) { +>isObject2 : Symbol(isObject2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 18, 5)) +>obj3 : Symbol(obj3, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 22, 13)) + + obj3; +>obj3 : Symbol(obj3, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 22, 13)) + + obj3['attr']; +>obj3 : Symbol(obj3, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 22, 13)) +} +// check type after conditional block +obj3; +>obj3 : Symbol(obj3, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 22, 13)) + +declare const obj4: Record | undefined; +>obj4 : Symbol(obj4, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 30, 13)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +if (isObject2(obj4)) { +>isObject2 : Symbol(isObject2, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 18, 5)) +>obj4 : Symbol(obj4, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 30, 13)) + + obj4; +>obj4 : Symbol(obj4, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 30, 13)) + + obj4['attr']; +>obj4 : Symbol(obj4, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 30, 13)) +} +// check type after conditional block +obj4; +>obj4 : Symbol(obj4, Decl(controlFlowFavorAssertedTypeThroughTypePredicate.ts, 30, 13)) + diff --git a/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.types b/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.types index a9902d995b991..395da42f87632 100644 --- a/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.types +++ b/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.types @@ -1,22 +1,19 @@ === tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts === // repro 49988#issuecomment-1192016929 -declare function isObject(value: unknown): value is Record; ->isObject : (value: unknown) => value is Record +declare function isObject1(value: unknown): value is Record; +>isObject1 : (value: unknown) => value is Record >value : unknown declare const obj1: {}; >obj1 : {} -obj1; // {} +if (isObject1(obj1)) { +>isObject1(obj1) : boolean +>isObject1 : (value: unknown) => value is Record >obj1 : {} -if (isObject(obj1)) { ->isObject(obj1) : boolean ->isObject : (value: unknown) => value is Record ->obj1 : {} - - obj1; // Record + obj1; >obj1 : Record obj1['attr']; @@ -24,21 +21,19 @@ if (isObject(obj1)) { >obj1 : Record >'attr' : "attr" } -obj1; // Record +// check type after conditional block +obj1; >obj1 : Record declare const obj2: {} | undefined; >obj2 : {} | undefined -obj2; // {} | undefined +if (isObject1(obj2)) { +>isObject1(obj2) : boolean +>isObject1 : (value: unknown) => value is Record >obj2 : {} | undefined -if (isObject(obj2)) { ->isObject(obj2) : boolean ->isObject : (value: unknown) => value is Record ->obj2 : {} | undefined - - obj2; // Record + obj2; >obj2 : Record obj2['attr']; @@ -46,6 +41,51 @@ if (isObject(obj2)) { >obj2 : Record >'attr' : "attr" } -obj2; // Record | undefined +// check type after conditional block +obj2; >obj2 : Record | undefined +declare function isObject2(value: unknown): value is {}; +>isObject2 : (value: unknown) => value is {} +>value : unknown + +declare const obj3: Record; +>obj3 : Record + +if (isObject2(obj3)) { +>isObject2(obj3) : boolean +>isObject2 : (value: unknown) => value is {} +>obj3 : Record + + obj3; +>obj3 : {} + + obj3['attr']; +>obj3['attr'] : any +>obj3 : {} +>'attr' : "attr" +} +// check type after conditional block +obj3; +>obj3 : {} + +declare const obj4: Record | undefined; +>obj4 : Record | undefined + +if (isObject2(obj4)) { +>isObject2(obj4) : boolean +>isObject2 : (value: unknown) => value is {} +>obj4 : Record | undefined + + obj4; +>obj4 : {} + + obj4['attr']; +>obj4['attr'] : any +>obj4 : {} +>'attr' : "attr" +} +// check type after conditional block +obj4; +>obj4 : {} | undefined + diff --git a/tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts b/tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts index e43f19485daca..d14faa3cfc565 100644 --- a/tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts +++ b/tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts @@ -3,20 +3,38 @@ // repro 49988#issuecomment-1192016929 -declare function isObject(value: unknown): value is Record; +declare function isObject1(value: unknown): value is Record; declare const obj1: {}; -obj1; // {} -if (isObject(obj1)) { - obj1; // Record +if (isObject1(obj1)) { + obj1; obj1['attr']; } -obj1; // Record +// check type after conditional block +obj1; declare const obj2: {} | undefined; -obj2; // {} | undefined -if (isObject(obj2)) { - obj2; // Record +if (isObject1(obj2)) { + obj2; obj2['attr']; } -obj2; // Record | undefined +// check type after conditional block +obj2; + +declare function isObject2(value: unknown): value is {}; + +declare const obj3: Record; +if (isObject2(obj3)) { + obj3; + obj3['attr']; +} +// check type after conditional block +obj3; + +declare const obj4: Record | undefined; +if (isObject2(obj4)) { + obj4; + obj4['attr']; +} +// check type after conditional block +obj4;