From 6c2e62b5784389155d711e6110f2ccd0e5d87efb Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 19 Jul 2022 12:32:06 -0700 Subject: [PATCH 1/5] Add test case with temporary name --- .../baselines/reference/issue49938.errors.txt | 26 ++++++++++ tests/baselines/reference/issue49938.js | 31 ++++++++++++ tests/baselines/reference/issue49938.symbols | 48 ++++++++++++++++++ tests/baselines/reference/issue49938.types | 50 +++++++++++++++++++ tests/cases/compiler/issue49938.ts | 19 +++++++ 5 files changed, 174 insertions(+) create mode 100644 tests/baselines/reference/issue49938.errors.txt create mode 100644 tests/baselines/reference/issue49938.js create mode 100644 tests/baselines/reference/issue49938.symbols create mode 100644 tests/baselines/reference/issue49938.types create mode 100644 tests/cases/compiler/issue49938.ts diff --git a/tests/baselines/reference/issue49938.errors.txt b/tests/baselines/reference/issue49938.errors.txt new file mode 100644 index 0000000000000..174a5fb951581 --- /dev/null +++ b/tests/baselines/reference/issue49938.errors.txt @@ -0,0 +1,26 @@ +tests/cases/compiler/issue49938.ts(12,15): error TS2345: Argument of type 'D | undefined' is not assignable to parameter of type 'B'. + Type 'undefined' is not assignable to type 'B'. + + +==== tests/cases/compiler/issue49938.ts (1 errors) ==== + function equal(a: T, b: T) { } + + let v = null!; + + // Object types with common base types + + type B = { foo: string } + type D = { foo: string; bar: number } + + // Error in 4.8 TS can't find common type ❌ + // 4.7 T was undefined | B + equal(v as B, v as undefined | D) + ~~~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type 'D | undefined' is not assignable to parameter of type 'B'. +!!! error TS2345: Type 'undefined' is not assignable to type 'B'. + + // ok T is B ✅ + equal(v as B, v as D) + // ok T is B | undefined ✅ + equal(v as B, v as B | undefined) + \ No newline at end of file diff --git a/tests/baselines/reference/issue49938.js b/tests/baselines/reference/issue49938.js new file mode 100644 index 0000000000000..e88099b9e4310 --- /dev/null +++ b/tests/baselines/reference/issue49938.js @@ -0,0 +1,31 @@ +//// [issue49938.ts] +function equal(a: T, b: T) { } + +let v = null!; + +// Object types with common base types + +type B = { foo: string } +type D = { foo: string; bar: number } + +// Error in 4.8 TS can't find common type ❌ +// 4.7 T was undefined | B +equal(v as B, v as undefined | D) + +// ok T is B ✅ +equal(v as B, v as D) +// ok T is B | undefined ✅ +equal(v as B, v as B | undefined) + + +//// [issue49938.js] +"use strict"; +function equal(a, b) { } +var v = null; +// Error in 4.8 TS can't find common type ❌ +// 4.7 T was undefined | B +equal(v, v); +// ok T is B ✅ +equal(v, v); +// ok T is B | undefined ✅ +equal(v, v); diff --git a/tests/baselines/reference/issue49938.symbols b/tests/baselines/reference/issue49938.symbols new file mode 100644 index 0000000000000..0ecb1601cf43f --- /dev/null +++ b/tests/baselines/reference/issue49938.symbols @@ -0,0 +1,48 @@ +=== tests/cases/compiler/issue49938.ts === +function equal(a: T, b: T) { } +>equal : Symbol(equal, Decl(issue49938.ts, 0, 0)) +>T : Symbol(T, Decl(issue49938.ts, 0, 15)) +>a : Symbol(a, Decl(issue49938.ts, 0, 18)) +>T : Symbol(T, Decl(issue49938.ts, 0, 15)) +>b : Symbol(b, Decl(issue49938.ts, 0, 23)) +>T : Symbol(T, Decl(issue49938.ts, 0, 15)) + +let v = null!; +>v : Symbol(v, Decl(issue49938.ts, 2, 3)) + +// Object types with common base types + +type B = { foo: string } +>B : Symbol(B, Decl(issue49938.ts, 2, 14)) +>foo : Symbol(foo, Decl(issue49938.ts, 6, 10)) + +type D = { foo: string; bar: number } +>D : Symbol(D, Decl(issue49938.ts, 6, 24)) +>foo : Symbol(foo, Decl(issue49938.ts, 7, 10)) +>bar : Symbol(bar, Decl(issue49938.ts, 7, 23)) + +// Error in 4.8 TS can't find common type ❌ +// 4.7 T was undefined | B +equal(v as B, v as undefined | D) +>equal : Symbol(equal, Decl(issue49938.ts, 0, 0)) +>v : Symbol(v, Decl(issue49938.ts, 2, 3)) +>B : Symbol(B, Decl(issue49938.ts, 2, 14)) +>v : Symbol(v, Decl(issue49938.ts, 2, 3)) +>D : Symbol(D, Decl(issue49938.ts, 6, 24)) + +// ok T is B ✅ +equal(v as B, v as D) +>equal : Symbol(equal, Decl(issue49938.ts, 0, 0)) +>v : Symbol(v, Decl(issue49938.ts, 2, 3)) +>B : Symbol(B, Decl(issue49938.ts, 2, 14)) +>v : Symbol(v, Decl(issue49938.ts, 2, 3)) +>D : Symbol(D, Decl(issue49938.ts, 6, 24)) + +// ok T is B | undefined ✅ +equal(v as B, v as B | undefined) +>equal : Symbol(equal, Decl(issue49938.ts, 0, 0)) +>v : Symbol(v, Decl(issue49938.ts, 2, 3)) +>B : Symbol(B, Decl(issue49938.ts, 2, 14)) +>v : Symbol(v, Decl(issue49938.ts, 2, 3)) +>B : Symbol(B, Decl(issue49938.ts, 2, 14)) + diff --git a/tests/baselines/reference/issue49938.types b/tests/baselines/reference/issue49938.types new file mode 100644 index 0000000000000..948dbb68af773 --- /dev/null +++ b/tests/baselines/reference/issue49938.types @@ -0,0 +1,50 @@ +=== tests/cases/compiler/issue49938.ts === +function equal(a: T, b: T) { } +>equal : (a: T, b: T) => void +>a : T +>b : T + +let v = null!; +>v : never +>null! : never +>null : null + +// Object types with common base types + +type B = { foo: string } +>B : { foo: string; } +>foo : string + +type D = { foo: string; bar: number } +>D : { foo: string; bar: number; } +>foo : string +>bar : number + +// Error in 4.8 TS can't find common type ❌ +// 4.7 T was undefined | B +equal(v as B, v as undefined | D) +>equal(v as B, v as undefined | D) : void +>equal : (a: T, b: T) => void +>v as B : B +>v : never +>v as undefined | D : D | undefined +>v : never + +// ok T is B ✅ +equal(v as B, v as D) +>equal(v as B, v as D) : void +>equal : (a: T, b: T) => void +>v as B : B +>v : never +>v as D : D +>v : never + +// ok T is B | undefined ✅ +equal(v as B, v as B | undefined) +>equal(v as B, v as B | undefined) : void +>equal : (a: T, b: T) => void +>v as B : B +>v : never +>v as B | undefined : B | undefined +>v : never + diff --git a/tests/cases/compiler/issue49938.ts b/tests/cases/compiler/issue49938.ts new file mode 100644 index 0000000000000..d7657a626d46a --- /dev/null +++ b/tests/cases/compiler/issue49938.ts @@ -0,0 +1,19 @@ +// @strict: true + +function equal(a: T, b: T) { } + +let v = null!; + +// Object types with common base types + +type B = { foo: string } +type D = { foo: string; bar: number } + +// Error in 4.8 TS can't find common type ❌ +// 4.7 T was undefined | B +equal(v as B, v as undefined | D) + +// ok T is B ✅ +equal(v as B, v as D) +// ok T is B | undefined ✅ +equal(v as B, v as B | undefined) From b288657dadeea1a68010b5932dd1820a90dc276c Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 20 Jul 2022 15:11:15 -0700 Subject: [PATCH 2/5] Rename and revamp test --- ...llableObjectTypesWithCommonBase.errors.txt | 38 +++++++ ...enceOfNullableObjectTypesWithCommonBase.js | 40 +++++++ ...fNullableObjectTypesWithCommonBase.symbols | 93 ++++++++++++++++ ...eOfNullableObjectTypesWithCommonBase.types | 102 ++++++++++++++++++ .../baselines/reference/issue49938.errors.txt | 26 ----- tests/baselines/reference/issue49938.js | 31 ------ tests/baselines/reference/issue49938.symbols | 48 --------- tests/baselines/reference/issue49938.types | 50 --------- ...enceOfNullableObjectTypesWithCommonBase.ts | 25 +++++ tests/cases/compiler/issue49938.ts | 19 ---- 10 files changed, 298 insertions(+), 174 deletions(-) create mode 100644 tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.errors.txt create mode 100644 tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.js create mode 100644 tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.symbols create mode 100644 tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.types delete mode 100644 tests/baselines/reference/issue49938.errors.txt delete mode 100644 tests/baselines/reference/issue49938.js delete mode 100644 tests/baselines/reference/issue49938.symbols delete mode 100644 tests/baselines/reference/issue49938.types create mode 100644 tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts delete mode 100644 tests/cases/compiler/issue49938.ts diff --git a/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.errors.txt b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.errors.txt new file mode 100644 index 0000000000000..b91f565e3b4be --- /dev/null +++ b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.errors.txt @@ -0,0 +1,38 @@ +tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts(10,15): error TS2345: Argument of type 'D | undefined' is not assignable to parameter of type 'B'. + Type 'undefined' is not assignable to type 'B'. +tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts(11,27): error TS2345: Argument of type 'B' is not assignable to parameter of type 'D'. + Property 'bar' is missing in type 'B' but required in type 'D'. + + +==== tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts (2 errors) ==== + function equal(a: T, b: T) { } + + let v = null!; + + // Object types with common base types + + type B = { foo: string } + type D = { foo: string; bar: number } + + equal(v as B, v as undefined | D) + ~~~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type 'D | undefined' is not assignable to parameter of type 'B'. +!!! error TS2345: Type 'undefined' is not assignable to type 'B'. + equal(v as undefined | D, v as B) + ~~~~~~ +!!! error TS2345: Argument of type 'B' is not assignable to parameter of type 'D'. +!!! error TS2345: Property 'bar' is missing in type 'B' but required in type 'D'. +!!! related TS2728 tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts:8:25: 'bar' is declared here. + + equal(v as B, v as undefined | D) + equal(v as undefined | D, v as B) + + equal(v as B, v as undefined) + equal(v as undefined, v as B) + + equal(v as B, v as D) + equal(v as D, v as B) + + equal(v as B, v as B | undefined) + equal(v as B | undefined, v as B) + \ No newline at end of file diff --git a/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.js b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.js new file mode 100644 index 0000000000000..9725e22509f84 --- /dev/null +++ b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.js @@ -0,0 +1,40 @@ +//// [inferenceOfNullableObjectTypesWithCommonBase.ts] +function equal(a: T, b: T) { } + +let v = null!; + +// Object types with common base types + +type B = { foo: string } +type D = { foo: string; bar: number } + +equal(v as B, v as undefined | D) +equal(v as undefined | D, v as B) + +equal(v as B, v as undefined | D) +equal(v as undefined | D, v as B) + +equal(v as B, v as undefined) +equal(v as undefined, v as B) + +equal(v as B, v as D) +equal(v as D, v as B) + +equal(v as B, v as B | undefined) +equal(v as B | undefined, v as B) + + +//// [inferenceOfNullableObjectTypesWithCommonBase.js] +"use strict"; +function equal(a, b) { } +var v = null; +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); diff --git a/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.symbols b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.symbols new file mode 100644 index 0000000000000..fe614f605469f --- /dev/null +++ b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.symbols @@ -0,0 +1,93 @@ +=== tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts === +function equal(a: T, b: T) { } +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>T : Symbol(T, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 15)) +>a : Symbol(a, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 18)) +>T : Symbol(T, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 15)) +>b : Symbol(b, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 23)) +>T : Symbol(T, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 15)) + +let v = null!; +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) + +// Object types with common base types + +type B = { foo: string } +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>foo : Symbol(foo, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 10)) + +type D = { foo: string; bar: number } +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) +>foo : Symbol(foo, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 7, 10)) +>bar : Symbol(bar, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 7, 23)) + +equal(v as B, v as undefined | D) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) + +equal(v as undefined | D, v as B) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) + +equal(v as B, v as undefined | D) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) + +equal(v as undefined | D, v as B) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) + +equal(v as B, v as undefined) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) + +equal(v as undefined, v as B) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) + +equal(v as B, v as D) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) + +equal(v as D, v as B) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) + +equal(v as B, v as B | undefined) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) + +equal(v as B | undefined, v as B) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) + diff --git a/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.types b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.types new file mode 100644 index 0000000000000..f78a78e838182 --- /dev/null +++ b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.types @@ -0,0 +1,102 @@ +=== tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts === +function equal(a: T, b: T) { } +>equal : (a: T, b: T) => void +>a : T +>b : T + +let v = null!; +>v : never +>null! : never +>null : null + +// Object types with common base types + +type B = { foo: string } +>B : { foo: string; } +>foo : string + +type D = { foo: string; bar: number } +>D : { foo: string; bar: number; } +>foo : string +>bar : number + +equal(v as B, v as undefined | D) +>equal(v as B, v as undefined | D) : void +>equal : (a: T, b: T) => void +>v as B : B +>v : never +>v as undefined | D : D | undefined +>v : never + +equal(v as undefined | D, v as B) +>equal(v as undefined | D, v as B) : void +>equal : (a: T, b: T) => void +>v as undefined | D : D | undefined +>v : never +>v as B : B +>v : never + +equal(v as B, v as undefined | D) +>equal(v as B, v as undefined | D) : void +>equal : (a: T, b: T) => void +>v as B : B +>v : never +>v as undefined | D : D | undefined +>v : never + +equal(v as undefined | D, v as B) +>equal(v as undefined | D, v as B) : void +>equal : (a: T, b: T) => void +>v as undefined | D : D | undefined +>v : never +>v as B : B +>v : never + +equal(v as B, v as undefined) +>equal(v as B, v as undefined) : void +>equal : (a: T, b: T) => void +>v as B : B +>v : never +>v as undefined : undefined +>v : never + +equal(v as undefined, v as B) +>equal(v as undefined, v as B) : void +>equal : (a: T, b: T) => void +>v as undefined : undefined +>v : never +>v as B : B +>v : never + +equal(v as B, v as D) +>equal(v as B, v as D) : void +>equal : (a: T, b: T) => void +>v as B : B +>v : never +>v as D : D +>v : never + +equal(v as D, v as B) +>equal(v as D, v as B) : void +>equal : (a: T, b: T) => void +>v as D : D +>v : never +>v as B : B +>v : never + +equal(v as B, v as B | undefined) +>equal(v as B, v as B | undefined) : void +>equal : (a: T, b: T) => void +>v as B : B +>v : never +>v as B | undefined : B | undefined +>v : never + +equal(v as B | undefined, v as B) +>equal(v as B | undefined, v as B) : void +>equal : (a: T, b: T) => void +>v as B | undefined : B | undefined +>v : never +>v as B : B +>v : never + diff --git a/tests/baselines/reference/issue49938.errors.txt b/tests/baselines/reference/issue49938.errors.txt deleted file mode 100644 index 174a5fb951581..0000000000000 --- a/tests/baselines/reference/issue49938.errors.txt +++ /dev/null @@ -1,26 +0,0 @@ -tests/cases/compiler/issue49938.ts(12,15): error TS2345: Argument of type 'D | undefined' is not assignable to parameter of type 'B'. - Type 'undefined' is not assignable to type 'B'. - - -==== tests/cases/compiler/issue49938.ts (1 errors) ==== - function equal(a: T, b: T) { } - - let v = null!; - - // Object types with common base types - - type B = { foo: string } - type D = { foo: string; bar: number } - - // Error in 4.8 TS can't find common type ❌ - // 4.7 T was undefined | B - equal(v as B, v as undefined | D) - ~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type 'D | undefined' is not assignable to parameter of type 'B'. -!!! error TS2345: Type 'undefined' is not assignable to type 'B'. - - // ok T is B ✅ - equal(v as B, v as D) - // ok T is B | undefined ✅ - equal(v as B, v as B | undefined) - \ No newline at end of file diff --git a/tests/baselines/reference/issue49938.js b/tests/baselines/reference/issue49938.js deleted file mode 100644 index e88099b9e4310..0000000000000 --- a/tests/baselines/reference/issue49938.js +++ /dev/null @@ -1,31 +0,0 @@ -//// [issue49938.ts] -function equal(a: T, b: T) { } - -let v = null!; - -// Object types with common base types - -type B = { foo: string } -type D = { foo: string; bar: number } - -// Error in 4.8 TS can't find common type ❌ -// 4.7 T was undefined | B -equal(v as B, v as undefined | D) - -// ok T is B ✅ -equal(v as B, v as D) -// ok T is B | undefined ✅ -equal(v as B, v as B | undefined) - - -//// [issue49938.js] -"use strict"; -function equal(a, b) { } -var v = null; -// Error in 4.8 TS can't find common type ❌ -// 4.7 T was undefined | B -equal(v, v); -// ok T is B ✅ -equal(v, v); -// ok T is B | undefined ✅ -equal(v, v); diff --git a/tests/baselines/reference/issue49938.symbols b/tests/baselines/reference/issue49938.symbols deleted file mode 100644 index 0ecb1601cf43f..0000000000000 --- a/tests/baselines/reference/issue49938.symbols +++ /dev/null @@ -1,48 +0,0 @@ -=== tests/cases/compiler/issue49938.ts === -function equal(a: T, b: T) { } ->equal : Symbol(equal, Decl(issue49938.ts, 0, 0)) ->T : Symbol(T, Decl(issue49938.ts, 0, 15)) ->a : Symbol(a, Decl(issue49938.ts, 0, 18)) ->T : Symbol(T, Decl(issue49938.ts, 0, 15)) ->b : Symbol(b, Decl(issue49938.ts, 0, 23)) ->T : Symbol(T, Decl(issue49938.ts, 0, 15)) - -let v = null!; ->v : Symbol(v, Decl(issue49938.ts, 2, 3)) - -// Object types with common base types - -type B = { foo: string } ->B : Symbol(B, Decl(issue49938.ts, 2, 14)) ->foo : Symbol(foo, Decl(issue49938.ts, 6, 10)) - -type D = { foo: string; bar: number } ->D : Symbol(D, Decl(issue49938.ts, 6, 24)) ->foo : Symbol(foo, Decl(issue49938.ts, 7, 10)) ->bar : Symbol(bar, Decl(issue49938.ts, 7, 23)) - -// Error in 4.8 TS can't find common type ❌ -// 4.7 T was undefined | B -equal(v as B, v as undefined | D) ->equal : Symbol(equal, Decl(issue49938.ts, 0, 0)) ->v : Symbol(v, Decl(issue49938.ts, 2, 3)) ->B : Symbol(B, Decl(issue49938.ts, 2, 14)) ->v : Symbol(v, Decl(issue49938.ts, 2, 3)) ->D : Symbol(D, Decl(issue49938.ts, 6, 24)) - -// ok T is B ✅ -equal(v as B, v as D) ->equal : Symbol(equal, Decl(issue49938.ts, 0, 0)) ->v : Symbol(v, Decl(issue49938.ts, 2, 3)) ->B : Symbol(B, Decl(issue49938.ts, 2, 14)) ->v : Symbol(v, Decl(issue49938.ts, 2, 3)) ->D : Symbol(D, Decl(issue49938.ts, 6, 24)) - -// ok T is B | undefined ✅ -equal(v as B, v as B | undefined) ->equal : Symbol(equal, Decl(issue49938.ts, 0, 0)) ->v : Symbol(v, Decl(issue49938.ts, 2, 3)) ->B : Symbol(B, Decl(issue49938.ts, 2, 14)) ->v : Symbol(v, Decl(issue49938.ts, 2, 3)) ->B : Symbol(B, Decl(issue49938.ts, 2, 14)) - diff --git a/tests/baselines/reference/issue49938.types b/tests/baselines/reference/issue49938.types deleted file mode 100644 index 948dbb68af773..0000000000000 --- a/tests/baselines/reference/issue49938.types +++ /dev/null @@ -1,50 +0,0 @@ -=== tests/cases/compiler/issue49938.ts === -function equal(a: T, b: T) { } ->equal : (a: T, b: T) => void ->a : T ->b : T - -let v = null!; ->v : never ->null! : never ->null : null - -// Object types with common base types - -type B = { foo: string } ->B : { foo: string; } ->foo : string - -type D = { foo: string; bar: number } ->D : { foo: string; bar: number; } ->foo : string ->bar : number - -// Error in 4.8 TS can't find common type ❌ -// 4.7 T was undefined | B -equal(v as B, v as undefined | D) ->equal(v as B, v as undefined | D) : void ->equal : (a: T, b: T) => void ->v as B : B ->v : never ->v as undefined | D : D | undefined ->v : never - -// ok T is B ✅ -equal(v as B, v as D) ->equal(v as B, v as D) : void ->equal : (a: T, b: T) => void ->v as B : B ->v : never ->v as D : D ->v : never - -// ok T is B | undefined ✅ -equal(v as B, v as B | undefined) ->equal(v as B, v as B | undefined) : void ->equal : (a: T, b: T) => void ->v as B : B ->v : never ->v as B | undefined : B | undefined ->v : never - diff --git a/tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts b/tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts new file mode 100644 index 0000000000000..950629a7b7f72 --- /dev/null +++ b/tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts @@ -0,0 +1,25 @@ +// @strict: true + +function equal(a: T, b: T) { } + +let v = null!; + +// Object types with common base types + +type B = { foo: string } +type D = { foo: string; bar: number } + +equal(v as B, v as undefined | D) +equal(v as undefined | D, v as B) + +equal(v as B, v as undefined | D) +equal(v as undefined | D, v as B) + +equal(v as B, v as undefined) +equal(v as undefined, v as B) + +equal(v as B, v as D) +equal(v as D, v as B) + +equal(v as B, v as B | undefined) +equal(v as B | undefined, v as B) diff --git a/tests/cases/compiler/issue49938.ts b/tests/cases/compiler/issue49938.ts deleted file mode 100644 index d7657a626d46a..0000000000000 --- a/tests/cases/compiler/issue49938.ts +++ /dev/null @@ -1,19 +0,0 @@ -// @strict: true - -function equal(a: T, b: T) { } - -let v = null!; - -// Object types with common base types - -type B = { foo: string } -type D = { foo: string; bar: number } - -// Error in 4.8 TS can't find common type ❌ -// 4.7 T was undefined | B -equal(v as B, v as undefined | D) - -// ok T is B ✅ -equal(v as B, v as D) -// ok T is B | undefined ✅ -equal(v as B, v as B | undefined) From 30d0cdc82bbb730bca7eeb5f0f08f8168280e29f Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 20 Jul 2022 16:30:58 -0700 Subject: [PATCH 3/5] Ensure common supertype retains nullability --- src/compiler/checker.ts | 18 ++++++++++++++++-- ...ullableObjectTypesWithCommonBase.errors.txt | 7 +------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 106142711d946..d33a5781a63d6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21228,7 +21228,17 @@ namespace ts { const primaryTypes = filter(types, t => !(t.flags & TypeFlags.Nullable)); if (primaryTypes.length) { const supertypeOrUnion = getSupertypeOrUnion(primaryTypes); - return primaryTypes === types ? supertypeOrUnion : getUnionType([supertypeOrUnion, ...filter(types, t => !!(t.flags & TypeFlags.Nullable))]); + const supertypeOrUnionFacts = getTypeFacts(supertypeOrUnion); + const allFacts = getAllTypeFacts(types); + + let missingNullableFlags: TypeFlags = 0; + if (allFacts & TypeFacts.IsNull && !(supertypeOrUnionFacts & TypeFacts.IsNull)) { + missingNullableFlags |= TypeFlags.Null; + } + if (allFacts & TypeFacts.IsUndefined && !(supertypeOrUnionFacts & TypeFacts.IsUndefined)) { + missingNullableFlags |= TypeFlags.Undefined; + } + return getNullableType(supertypeOrUnion, missingNullableFlags); } return getUnionType(types, UnionReduction.Subtype); } @@ -23717,7 +23727,7 @@ namespace ts { return TypeFacts.None; } if (flags & TypeFlags.Union) { - return reduceLeft((type as UnionType).types, (facts, t) => facts | getTypeFacts(t), TypeFacts.None); + return getAllTypeFacts((type as UnionType).types); } if (flags & TypeFlags.Intersection) { return getIntersectionTypeFacts(type as IntersectionType); @@ -23725,6 +23735,10 @@ namespace ts { return TypeFacts.UnknownFacts; } + function getAllTypeFacts(types: Type[]): TypeFacts { + return reduceLeft(types, (facts, t) => facts | getTypeFacts(t), TypeFacts.None); + } + function getIntersectionTypeFacts(type: IntersectionType): TypeFacts { // When an intersection contains a primitive type we ignore object type constituents as they are // presumably type tags. For example, in string & { __kind__: "name" } we ignore the object type. diff --git a/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.errors.txt b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.errors.txt index b91f565e3b4be..27267320ce4e4 100644 --- a/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.errors.txt +++ b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.errors.txt @@ -1,10 +1,8 @@ -tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts(10,15): error TS2345: Argument of type 'D | undefined' is not assignable to parameter of type 'B'. - Type 'undefined' is not assignable to type 'B'. tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts(11,27): error TS2345: Argument of type 'B' is not assignable to parameter of type 'D'. Property 'bar' is missing in type 'B' but required in type 'D'. -==== tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts (2 errors) ==== +==== tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts (1 errors) ==== function equal(a: T, b: T) { } let v = null!; @@ -15,9 +13,6 @@ tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts(11,27): err type D = { foo: string; bar: number } equal(v as B, v as undefined | D) - ~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type 'D | undefined' is not assignable to parameter of type 'B'. -!!! error TS2345: Type 'undefined' is not assignable to type 'B'. equal(v as undefined | D, v as B) ~~~~~~ !!! error TS2345: Argument of type 'B' is not assignable to parameter of type 'D'. From 1b96117c1269d5825d6276b07c8c611b1454c15f Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 21 Jul 2022 09:40:47 -0700 Subject: [PATCH 4/5] Avoid producing brand new types in getCommonSupertype --- src/compiler/checker.ts | 27 +++++++-------- ...llableObjectTypesWithCommonBase.errors.txt | 33 ------------------- 2 files changed, 12 insertions(+), 48 deletions(-) delete mode 100644 tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d33a5781a63d6..da6dc98afb37d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21218,29 +21218,26 @@ namespace ts { } return literalTypesWithSameBaseType(types) ? getUnionType(types) : - reduceLeft(types, (s, t) => isTypeSubtypeOf(s, t) ? t : s)!; + reduceLeft(types, (s, t) => isTypeSubtypeOf(getNonNullableTypeIfNeeded(s), getNonNullableTypeIfNeeded(t)) ? t : s)!; } function getCommonSupertype(types: Type[]): Type { if (!strictNullChecks) { return getSupertypeOrUnion(types); } - const primaryTypes = filter(types, t => !(t.flags & TypeFlags.Nullable)); - if (primaryTypes.length) { - const supertypeOrUnion = getSupertypeOrUnion(primaryTypes); - const supertypeOrUnionFacts = getTypeFacts(supertypeOrUnion); - const allFacts = getAllTypeFacts(types); - let missingNullableFlags: TypeFlags = 0; - if (allFacts & TypeFacts.IsNull && !(supertypeOrUnionFacts & TypeFacts.IsNull)) { - missingNullableFlags |= TypeFlags.Null; - } - if (allFacts & TypeFacts.IsUndefined && !(supertypeOrUnionFacts & TypeFacts.IsUndefined)) { - missingNullableFlags |= TypeFlags.Undefined; - } - return getNullableType(supertypeOrUnion, missingNullableFlags); + const supertypeOrUnion = getSupertypeOrUnion(types); + const supertypeOrUnionFacts = getTypeFacts(supertypeOrUnion); + const allFacts = getAllTypeFacts(types); + + let missingNullableFlags: TypeFlags = 0; + if (allFacts & TypeFacts.IsNull && !(supertypeOrUnionFacts & TypeFacts.IsNull)) { + missingNullableFlags |= TypeFlags.Null; + } + if (allFacts & TypeFacts.IsUndefined && !(supertypeOrUnionFacts & TypeFacts.IsUndefined)) { + missingNullableFlags |= TypeFlags.Undefined; } - return getUnionType(types, UnionReduction.Subtype); + return getNullableType(supertypeOrUnion, missingNullableFlags); } // Return the leftmost type for which no type to the right is a subtype. diff --git a/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.errors.txt b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.errors.txt deleted file mode 100644 index 27267320ce4e4..0000000000000 --- a/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.errors.txt +++ /dev/null @@ -1,33 +0,0 @@ -tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts(11,27): error TS2345: Argument of type 'B' is not assignable to parameter of type 'D'. - Property 'bar' is missing in type 'B' but required in type 'D'. - - -==== tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts (1 errors) ==== - function equal(a: T, b: T) { } - - let v = null!; - - // Object types with common base types - - type B = { foo: string } - type D = { foo: string; bar: number } - - equal(v as B, v as undefined | D) - equal(v as undefined | D, v as B) - ~~~~~~ -!!! error TS2345: Argument of type 'B' is not assignable to parameter of type 'D'. -!!! error TS2345: Property 'bar' is missing in type 'B' but required in type 'D'. -!!! related TS2728 tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts:8:25: 'bar' is declared here. - - equal(v as B, v as undefined | D) - equal(v as undefined | D, v as B) - - equal(v as B, v as undefined) - equal(v as undefined, v as B) - - equal(v as B, v as D) - equal(v as D, v as B) - - equal(v as B, v as B | undefined) - equal(v as B | undefined, v as B) - \ No newline at end of file From a8174354cf150ff660037a2c4b1d62a55720c30c Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 21 Jul 2022 11:01:39 -0700 Subject: [PATCH 5/5] Only check for undefined or null directly or in unions --- src/compiler/checker.ts | 19 +-- .../inferenceDoesNotAddUndefinedOrNull.js | 55 ++++++++ ...inferenceDoesNotAddUndefinedOrNull.symbols | 127 ++++++++++++++++++ .../inferenceDoesNotAddUndefinedOrNull.types | 109 +++++++++++++++ .../inferenceDoesNotAddUndefinedOrNull.ts | 32 +++++ 5 files changed, 333 insertions(+), 9 deletions(-) create mode 100644 tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.js create mode 100644 tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.symbols create mode 100644 tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.types create mode 100644 tests/cases/compiler/inferenceDoesNotAddUndefinedOrNull.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index da6dc98afb37d..c1246f04e842a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21227,17 +21227,22 @@ namespace ts { } const supertypeOrUnion = getSupertypeOrUnion(types); - const supertypeOrUnionFacts = getTypeFacts(supertypeOrUnion); - const allFacts = getAllTypeFacts(types); + + const shouldHaveNull = some(types, t => someHas(t, TypeFlags.Null)); + const shouldHaveUndefined = some(types, t => someHas(t, TypeFlags.Undefined)); let missingNullableFlags: TypeFlags = 0; - if (allFacts & TypeFacts.IsNull && !(supertypeOrUnionFacts & TypeFacts.IsNull)) { + if (shouldHaveNull && !someHas(supertypeOrUnion, TypeFlags.Null)) { missingNullableFlags |= TypeFlags.Null; } - if (allFacts & TypeFacts.IsUndefined && !(supertypeOrUnionFacts & TypeFacts.IsUndefined)) { + if (shouldHaveUndefined && !someHas(supertypeOrUnion, TypeFlags.Undefined)) { missingNullableFlags |= TypeFlags.Undefined; } return getNullableType(supertypeOrUnion, missingNullableFlags); + + function someHas(type: Type, flags: TypeFlags): boolean { + return someType(type, t => !!(t.flags & flags)); + } } // Return the leftmost type for which no type to the right is a subtype. @@ -23724,7 +23729,7 @@ namespace ts { return TypeFacts.None; } if (flags & TypeFlags.Union) { - return getAllTypeFacts((type as UnionType).types); + return reduceLeft((type as UnionType).types, (facts, t) => facts | getTypeFacts(t), TypeFacts.None); } if (flags & TypeFlags.Intersection) { return getIntersectionTypeFacts(type as IntersectionType); @@ -23732,10 +23737,6 @@ namespace ts { return TypeFacts.UnknownFacts; } - function getAllTypeFacts(types: Type[]): TypeFacts { - return reduceLeft(types, (facts, t) => facts | getTypeFacts(t), TypeFacts.None); - } - function getIntersectionTypeFacts(type: IntersectionType): TypeFacts { // When an intersection contains a primitive type we ignore object type constituents as they are // presumably type tags. For example, in string & { __kind__: "name" } we ignore the object type. diff --git a/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.js b/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.js new file mode 100644 index 0000000000000..5323e2dfe5585 --- /dev/null +++ b/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.js @@ -0,0 +1,55 @@ +//// [inferenceDoesNotAddUndefinedOrNull.ts] +interface NodeArray extends ReadonlyArray {} + +interface Node { + forEachChild(cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray) => T | undefined): T | undefined; +} + +declare function toArray(value: T | T[]): T[]; +declare function toArray(value: T | readonly T[]): readonly T[]; + +function flatMapChildren(node: Node, cb: (child: Node) => readonly T[] | T | undefined): readonly T[] { + const result: T[] = []; + node.forEachChild(child => { + const value = cb(child); + if (value !== undefined) { + result.push(...toArray(value)); + } + }); + return result; +} + +function flatMapChildren2(node: Node, cb: (child: Node) => readonly T[] | T | null): readonly T[] { + const result: T[] = []; + node.forEachChild(child => { + const value = cb(child); + if (value !== null) { + result.push(...toArray(value)); + } + }); + return result; +} + + +//// [inferenceDoesNotAddUndefinedOrNull.js] +"use strict"; +function flatMapChildren(node, cb) { + var result = []; + node.forEachChild(function (child) { + var value = cb(child); + if (value !== undefined) { + result.push.apply(result, toArray(value)); + } + }); + return result; +} +function flatMapChildren2(node, cb) { + var result = []; + node.forEachChild(function (child) { + var value = cb(child); + if (value !== null) { + result.push.apply(result, toArray(value)); + } + }); + return result; +} diff --git a/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.symbols b/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.symbols new file mode 100644 index 0000000000000..96d07b95cc00b --- /dev/null +++ b/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.symbols @@ -0,0 +1,127 @@ +=== tests/cases/compiler/inferenceDoesNotAddUndefinedOrNull.ts === +interface NodeArray extends ReadonlyArray {} +>NodeArray : Symbol(NodeArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 0)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 20)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 20)) + +interface Node { +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) + + forEachChild(cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray) => T | undefined): T | undefined; +>forEachChild : Symbol(Node.forEachChild, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 2, 16)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 17)) +>cbNode : Symbol(cbNode, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 20)) +>node : Symbol(node, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 29)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 17)) +>cbNodeArray : Symbol(cbNodeArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 58)) +>nodes : Symbol(nodes, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 74)) +>NodeArray : Symbol(NodeArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 0)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 17)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 17)) +} + +declare function toArray(value: T | T[]): T[]; +>toArray : Symbol(toArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 4, 1), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 49)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 25)) +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 28)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 25)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 25)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 25)) + +declare function toArray(value: T | readonly T[]): readonly T[]; +>toArray : Symbol(toArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 4, 1), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 49)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 7, 25)) +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 7, 28)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 7, 25)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 7, 25)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 7, 25)) + +function flatMapChildren(node: Node, cb: (child: Node) => readonly T[] | T | undefined): readonly T[] { +>flatMapChildren : Symbol(flatMapChildren, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 7, 67)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 25)) +>node : Symbol(node, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 28)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>cb : Symbol(cb, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 39)) +>child : Symbol(child, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 45)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 25)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 25)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 25)) + + const result: T[] = []; +>result : Symbol(result, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 10, 9)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 25)) + + node.forEachChild(child => { +>node.forEachChild : Symbol(Node.forEachChild, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 2, 16)) +>node : Symbol(node, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 28)) +>forEachChild : Symbol(Node.forEachChild, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 2, 16)) +>child : Symbol(child, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 11, 22)) + + const value = cb(child); +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 12, 13)) +>cb : Symbol(cb, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 39)) +>child : Symbol(child, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 11, 22)) + + if (value !== undefined) { +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 12, 13)) +>undefined : Symbol(undefined) + + result.push(...toArray(value)); +>result.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>result : Symbol(result, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 10, 9)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>toArray : Symbol(toArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 4, 1), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 49)) +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 12, 13)) + } + }); + return result; +>result : Symbol(result, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 10, 9)) +} + +function flatMapChildren2(node: Node, cb: (child: Node) => readonly T[] | T | null): readonly T[] { +>flatMapChildren2 : Symbol(flatMapChildren2, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 18, 1)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 26)) +>node : Symbol(node, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 29)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>cb : Symbol(cb, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 40)) +>child : Symbol(child, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 46)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 26)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 26)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 26)) + + const result: T[] = []; +>result : Symbol(result, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 21, 9)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 26)) + + node.forEachChild(child => { +>node.forEachChild : Symbol(Node.forEachChild, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 2, 16)) +>node : Symbol(node, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 29)) +>forEachChild : Symbol(Node.forEachChild, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 2, 16)) +>child : Symbol(child, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 22, 22)) + + const value = cb(child); +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 23, 13)) +>cb : Symbol(cb, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 40)) +>child : Symbol(child, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 22, 22)) + + if (value !== null) { +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 23, 13)) + + result.push(...toArray(value)); +>result.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>result : Symbol(result, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 21, 9)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>toArray : Symbol(toArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 4, 1), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 49)) +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 23, 13)) + } + }); + return result; +>result : Symbol(result, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 21, 9)) +} + diff --git a/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.types b/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.types new file mode 100644 index 0000000000000..a36bafa23e748 --- /dev/null +++ b/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.types @@ -0,0 +1,109 @@ +=== tests/cases/compiler/inferenceDoesNotAddUndefinedOrNull.ts === +interface NodeArray extends ReadonlyArray {} + +interface Node { + forEachChild(cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray) => T | undefined): T | undefined; +>forEachChild : (cbNode: (node: Node) => T | undefined, cbNodeArray?: ((nodes: NodeArray) => T | undefined) | undefined) => T | undefined +>cbNode : (node: Node) => T | undefined +>node : Node +>cbNodeArray : ((nodes: NodeArray) => T | undefined) | undefined +>nodes : NodeArray +} + +declare function toArray(value: T | T[]): T[]; +>toArray : { (value: T | T[]): T[]; (value: T | readonly T[]): readonly T[]; } +>value : T | T[] + +declare function toArray(value: T | readonly T[]): readonly T[]; +>toArray : { (value: T | T[]): T[]; (value: T | readonly T[]): readonly T[]; } +>value : T | readonly T[] + +function flatMapChildren(node: Node, cb: (child: Node) => readonly T[] | T | undefined): readonly T[] { +>flatMapChildren : (node: Node, cb: (child: Node) => readonly T[] | T | undefined) => readonly T[] +>node : Node +>cb : (child: Node) => readonly T[] | T | undefined +>child : Node + + const result: T[] = []; +>result : T[] +>[] : never[] + + node.forEachChild(child => { +>node.forEachChild(child => { const value = cb(child); if (value !== undefined) { result.push(...toArray(value)); } }) : void | undefined +>node.forEachChild : (cbNode: (node: Node) => T | undefined, cbNodeArray?: ((nodes: NodeArray) => T | undefined) | undefined) => T | undefined +>node : Node +>forEachChild : (cbNode: (node: Node) => T | undefined, cbNodeArray?: ((nodes: NodeArray) => T | undefined) | undefined) => T | undefined +>child => { const value = cb(child); if (value !== undefined) { result.push(...toArray(value)); } } : (child: Node) => void +>child : Node + + const value = cb(child); +>value : T | readonly T[] | undefined +>cb(child) : T | readonly T[] | undefined +>cb : (child: Node) => T | readonly T[] | undefined +>child : Node + + if (value !== undefined) { +>value !== undefined : boolean +>value : T | readonly T[] | undefined +>undefined : undefined + + result.push(...toArray(value)); +>result.push(...toArray(value)) : number +>result.push : (...items: T[]) => number +>result : T[] +>push : (...items: T[]) => number +>...toArray(value) : T +>toArray(value) : readonly T[] +>toArray : { (value: T | T[]): T[]; (value: T | readonly T[]): readonly T[]; } +>value : readonly T[] | (T & ({} | null)) + } + }); + return result; +>result : T[] +} + +function flatMapChildren2(node: Node, cb: (child: Node) => readonly T[] | T | null): readonly T[] { +>flatMapChildren2 : (node: Node, cb: (child: Node) => readonly T[] | T | null) => readonly T[] +>node : Node +>cb : (child: Node) => readonly T[] | T | null +>child : Node +>null : null + + const result: T[] = []; +>result : T[] +>[] : never[] + + node.forEachChild(child => { +>node.forEachChild(child => { const value = cb(child); if (value !== null) { result.push(...toArray(value)); } }) : void | undefined +>node.forEachChild : (cbNode: (node: Node) => T | undefined, cbNodeArray?: ((nodes: NodeArray) => T | undefined) | undefined) => T | undefined +>node : Node +>forEachChild : (cbNode: (node: Node) => T | undefined, cbNodeArray?: ((nodes: NodeArray) => T | undefined) | undefined) => T | undefined +>child => { const value = cb(child); if (value !== null) { result.push(...toArray(value)); } } : (child: Node) => void +>child : Node + + const value = cb(child); +>value : T | readonly T[] | null +>cb(child) : T | readonly T[] | null +>cb : (child: Node) => T | readonly T[] | null +>child : Node + + if (value !== null) { +>value !== null : boolean +>value : T | readonly T[] | null +>null : null + + result.push(...toArray(value)); +>result.push(...toArray(value)) : number +>result.push : (...items: T[]) => number +>result : T[] +>push : (...items: T[]) => number +>...toArray(value) : T +>toArray(value) : readonly T[] +>toArray : { (value: T | T[]): T[]; (value: T | readonly T[]): readonly T[]; } +>value : readonly T[] | (T & ({} | undefined)) + } + }); + return result; +>result : T[] +} + diff --git a/tests/cases/compiler/inferenceDoesNotAddUndefinedOrNull.ts b/tests/cases/compiler/inferenceDoesNotAddUndefinedOrNull.ts new file mode 100644 index 0000000000000..75f53691a2511 --- /dev/null +++ b/tests/cases/compiler/inferenceDoesNotAddUndefinedOrNull.ts @@ -0,0 +1,32 @@ +// @strict: true + +interface NodeArray extends ReadonlyArray {} + +interface Node { + forEachChild(cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray) => T | undefined): T | undefined; +} + +declare function toArray(value: T | T[]): T[]; +declare function toArray(value: T | readonly T[]): readonly T[]; + +function flatMapChildren(node: Node, cb: (child: Node) => readonly T[] | T | undefined): readonly T[] { + const result: T[] = []; + node.forEachChild(child => { + const value = cb(child); + if (value !== undefined) { + result.push(...toArray(value)); + } + }); + return result; +} + +function flatMapChildren2(node: Node, cb: (child: Node) => readonly T[] | T | null): readonly T[] { + const result: T[] = []; + node.forEachChild(child => { + const value = cb(child); + if (value !== null) { + result.push(...toArray(value)); + } + }); + return result; +}