diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bf7d17a951f72..3de21bebf7681 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23094,10 +23094,15 @@ namespace ts { } } - // If the candidate type is a subtype of the target type, narrow to the candidate type, - // if the target type is a subtype of the candidate type, narrow to the target type, - // otherwise, narrow to an intersection of the two types. - return isTypeSubtypeOf(candidate, type) ? candidate : isTypeSubtypeOf(type, candidate) ? type : getIntersectionType([type, candidate]); + // If the candidate type is a subtype of the target type, narrow to the candidate type. + // Otherwise, if the target type is assignable to the candidate type, keep the target type. + // Otherwise, if the candidate type is assignable to the target type, narrow to the candidate + // type. Otherwise, the types are completely unrelated, so narrow to an intersection of the + // two types. + return isTypeSubtypeOf(candidate, type) ? candidate : + isTypeAssignableTo(type, candidate) ? type : + isTypeAssignableTo(candidate, type) ? candidate : + getIntersectionType([type, candidate]); } function narrowTypeByCallExpression(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { diff --git a/tests/baselines/reference/instanceOfAssignability.types b/tests/baselines/reference/instanceOfAssignability.types index 9c29abbe89491..4e65e644a3b54 100644 --- a/tests/baselines/reference/instanceOfAssignability.types +++ b/tests/baselines/reference/instanceOfAssignability.types @@ -70,8 +70,8 @@ function fn2(x: Base) { // 1.5: y: Base // Want: y: Derived1 let y = x; ->y : Base & Derived1 ->x : Base & Derived1 +>y : Derived1 +>x : Derived1 } } @@ -104,8 +104,8 @@ function fn4(x: Base|Derived2) { // 1.5: y: {} // Want: Derived1 let y = x; ->y : (Base | Derived2) & Derived1 ->x : (Base | Derived2) & Derived1 +>y : Derived1 +>x : Derived1 } } diff --git a/tests/baselines/reference/narrowingAssignmentReadonlyRespectsAssertion.js b/tests/baselines/reference/narrowingAssignmentReadonlyRespectsAssertion.js new file mode 100644 index 0000000000000..5db0ab74ca7ae --- /dev/null +++ b/tests/baselines/reference/narrowingAssignmentReadonlyRespectsAssertion.js @@ -0,0 +1,71 @@ +//// [narrowingAssignmentReadonlyRespectsAssertion.ts] +// https://github.com/microsoft/TypeScript/issues/41984 + +interface TestCase { + readonly val1: T | ReadonlyArray; + readonly val2: ReadonlyArray; +} + +interface MultiCaseFixture { + cases: T[]; +} + +function subDataFunc(): TestCase[] { + return [ + { val1: "a", val2: ["a", "b", "c"] }, + { val1: 2, val2: [1, 2, 3] }, + { val1: ["a", "z"], val2: ["x", "y", "z"] }, + { val1: [5, 10], val2: [10, 100, 1000] }, + ]; +} + +function dataFunc(subFunc: () => T[]): MultiCaseFixture { + return { cases: subFunc() }; +} + +function testFunc() { + const fixture = dataFunc>(subDataFunc); + fixture.cases.forEach(({ val1, val2 }) => { + if (Array.isArray(val1)) { + // This should retain val1 as being an array + const reversedVal1 = val1.slice().reverse(); + console.log(reversedVal1); + } else { + console.log(val1); + } + console.log(val2); + }); +} + +testFunc(); + + +//// [narrowingAssignmentReadonlyRespectsAssertion.js] +// https://github.com/microsoft/TypeScript/issues/41984 +function subDataFunc() { + return [ + { val1: "a", val2: ["a", "b", "c"] }, + { val1: 2, val2: [1, 2, 3] }, + { val1: ["a", "z"], val2: ["x", "y", "z"] }, + { val1: [5, 10], val2: [10, 100, 1000] }, + ]; +} +function dataFunc(subFunc) { + return { cases: subFunc() }; +} +function testFunc() { + var fixture = dataFunc(subDataFunc); + fixture.cases.forEach(function (_a) { + var val1 = _a.val1, val2 = _a.val2; + if (Array.isArray(val1)) { + // This should retain val1 as being an array + var reversedVal1 = val1.slice().reverse(); + console.log(reversedVal1); + } + else { + console.log(val1); + } + console.log(val2); + }); +} +testFunc(); diff --git a/tests/baselines/reference/narrowingAssignmentReadonlyRespectsAssertion.symbols b/tests/baselines/reference/narrowingAssignmentReadonlyRespectsAssertion.symbols new file mode 100644 index 0000000000000..8b7fcfebae44f --- /dev/null +++ b/tests/baselines/reference/narrowingAssignmentReadonlyRespectsAssertion.symbols @@ -0,0 +1,123 @@ +=== tests/cases/compiler/narrowingAssignmentReadonlyRespectsAssertion.ts === +// https://github.com/microsoft/TypeScript/issues/41984 + +interface TestCase { +>TestCase : Symbol(TestCase, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 0, 0)) +>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 2, 19)) + + readonly val1: T | ReadonlyArray; +>val1 : Symbol(TestCase.val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 2, 47)) +>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 2, 19)) +>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 2, 19)) + + readonly val2: ReadonlyArray; +>val2 : Symbol(TestCase.val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 3, 38)) +>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 2, 19)) +} + +interface MultiCaseFixture { +>MultiCaseFixture : Symbol(MultiCaseFixture, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 5, 1)) +>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 7, 27)) + + cases: T[]; +>cases : Symbol(MultiCaseFixture.cases, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 7, 31)) +>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 7, 27)) +} + +function subDataFunc(): TestCase[] { +>subDataFunc : Symbol(subDataFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 9, 1)) +>TestCase : Symbol(TestCase, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 0, 0)) + + return [ + { val1: "a", val2: ["a", "b", "c"] }, +>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 13, 7)) +>val2 : Symbol(val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 13, 18)) + + { val1: 2, val2: [1, 2, 3] }, +>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 14, 7)) +>val2 : Symbol(val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 14, 16)) + + { val1: ["a", "z"], val2: ["x", "y", "z"] }, +>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 15, 7)) +>val2 : Symbol(val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 15, 25)) + + { val1: [5, 10], val2: [10, 100, 1000] }, +>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 16, 7)) +>val2 : Symbol(val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 16, 22)) + + ]; +} + +function dataFunc(subFunc: () => T[]): MultiCaseFixture { +>dataFunc : Symbol(dataFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 18, 1)) +>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 20, 18)) +>subFunc : Symbol(subFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 20, 21)) +>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 20, 18)) +>MultiCaseFixture : Symbol(MultiCaseFixture, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 5, 1)) +>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 20, 18)) + + return { cases: subFunc() }; +>cases : Symbol(cases, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 21, 10)) +>subFunc : Symbol(subFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 20, 21)) +} + +function testFunc() { +>testFunc : Symbol(testFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 22, 1)) + + const fixture = dataFunc>(subDataFunc); +>fixture : Symbol(fixture, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 25, 7)) +>dataFunc : Symbol(dataFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 18, 1)) +>TestCase : Symbol(TestCase, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 0, 0)) +>subDataFunc : Symbol(subDataFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 9, 1)) + + fixture.cases.forEach(({ val1, val2 }) => { +>fixture.cases.forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>fixture.cases : Symbol(MultiCaseFixture.cases, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 7, 31)) +>fixture : Symbol(fixture, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 25, 7)) +>cases : Symbol(MultiCaseFixture.cases, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 7, 31)) +>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 26, 26)) +>val2 : Symbol(val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 26, 32)) + + if (Array.isArray(val1)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 26, 26)) + + // This should retain val1 as being an array + const reversedVal1 = val1.slice().reverse(); +>reversedVal1 : Symbol(reversedVal1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 29, 15)) +>val1.slice().reverse : Symbol(Array.reverse, Decl(lib.es5.d.ts, --, --)) +>val1.slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --)) +>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 26, 26)) +>slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --)) +>reverse : Symbol(Array.reverse, Decl(lib.es5.d.ts, --, --)) + + console.log(reversedVal1); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>reversedVal1 : Symbol(reversedVal1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 29, 15)) + + } else { + console.log(val1); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>val1 : Symbol(val1, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 26, 26)) + } + console.log(val2); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>val2 : Symbol(val2, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 26, 32)) + + }); +} + +testFunc(); +>testFunc : Symbol(testFunc, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 22, 1)) + diff --git a/tests/baselines/reference/narrowingAssignmentReadonlyRespectsAssertion.types b/tests/baselines/reference/narrowingAssignmentReadonlyRespectsAssertion.types new file mode 100644 index 0000000000000..9fd13d661027a --- /dev/null +++ b/tests/baselines/reference/narrowingAssignmentReadonlyRespectsAssertion.types @@ -0,0 +1,147 @@ +=== tests/cases/compiler/narrowingAssignmentReadonlyRespectsAssertion.ts === +// https://github.com/microsoft/TypeScript/issues/41984 + +interface TestCase { + readonly val1: T | ReadonlyArray; +>val1 : T | readonly T[] + + readonly val2: ReadonlyArray; +>val2 : readonly T[] +} + +interface MultiCaseFixture { + cases: T[]; +>cases : T[] +} + +function subDataFunc(): TestCase[] { +>subDataFunc : () => TestCase[] + + return [ +>[ { val1: "a", val2: ["a", "b", "c"] }, { val1: 2, val2: [1, 2, 3] }, { val1: ["a", "z"], val2: ["x", "y", "z"] }, { val1: [5, 10], val2: [10, 100, 1000] }, ] : ({ val1: string; val2: string[]; } | { val1: number; val2: number[]; } | { val1: string[]; val2: string[]; } | { val1: number[]; val2: number[]; })[] + + { val1: "a", val2: ["a", "b", "c"] }, +>{ val1: "a", val2: ["a", "b", "c"] } : { val1: string; val2: string[]; } +>val1 : string +>"a" : "a" +>val2 : string[] +>["a", "b", "c"] : string[] +>"a" : "a" +>"b" : "b" +>"c" : "c" + + { val1: 2, val2: [1, 2, 3] }, +>{ val1: 2, val2: [1, 2, 3] } : { val1: number; val2: number[]; } +>val1 : number +>2 : 2 +>val2 : number[] +>[1, 2, 3] : number[] +>1 : 1 +>2 : 2 +>3 : 3 + + { val1: ["a", "z"], val2: ["x", "y", "z"] }, +>{ val1: ["a", "z"], val2: ["x", "y", "z"] } : { val1: string[]; val2: string[]; } +>val1 : string[] +>["a", "z"] : string[] +>"a" : "a" +>"z" : "z" +>val2 : string[] +>["x", "y", "z"] : string[] +>"x" : "x" +>"y" : "y" +>"z" : "z" + + { val1: [5, 10], val2: [10, 100, 1000] }, +>{ val1: [5, 10], val2: [10, 100, 1000] } : { val1: number[]; val2: number[]; } +>val1 : number[] +>[5, 10] : number[] +>5 : 5 +>10 : 10 +>val2 : number[] +>[10, 100, 1000] : number[] +>10 : 10 +>100 : 100 +>1000 : 1000 + + ]; +} + +function dataFunc(subFunc: () => T[]): MultiCaseFixture { +>dataFunc : (subFunc: () => T[]) => MultiCaseFixture +>subFunc : () => T[] + + return { cases: subFunc() }; +>{ cases: subFunc() } : { cases: T[]; } +>cases : T[] +>subFunc() : T[] +>subFunc : () => T[] +} + +function testFunc() { +>testFunc : () => void + + const fixture = dataFunc>(subDataFunc); +>fixture : MultiCaseFixture> +>dataFunc>(subDataFunc) : MultiCaseFixture> +>dataFunc : (subFunc: () => T[]) => MultiCaseFixture +>subDataFunc : () => TestCase[] + + fixture.cases.forEach(({ val1, val2 }) => { +>fixture.cases.forEach(({ val1, val2 }) => { if (Array.isArray(val1)) { // This should retain val1 as being an array const reversedVal1 = val1.slice().reverse(); console.log(reversedVal1); } else { console.log(val1); } console.log(val2); }) : void +>fixture.cases.forEach : (callbackfn: (value: TestCase, index: number, array: TestCase[]) => void, thisArg?: any) => void +>fixture.cases : TestCase[] +>fixture : MultiCaseFixture> +>cases : TestCase[] +>forEach : (callbackfn: (value: TestCase, index: number, array: TestCase[]) => void, thisArg?: any) => void +>({ val1, val2 }) => { if (Array.isArray(val1)) { // This should retain val1 as being an array const reversedVal1 = val1.slice().reverse(); console.log(reversedVal1); } else { console.log(val1); } console.log(val2); } : ({ val1, val2 }: TestCase) => void +>val1 : string | number | readonly (string | number)[] +>val2 : readonly (string | number)[] + + if (Array.isArray(val1)) { +>Array.isArray(val1) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>val1 : string | number | readonly (string | number)[] + + // This should retain val1 as being an array + const reversedVal1 = val1.slice().reverse(); +>reversedVal1 : any[] +>val1.slice().reverse() : any[] +>val1.slice().reverse : () => any[] +>val1.slice() : any[] +>val1.slice : (start?: number, end?: number) => any[] +>val1 : any[] +>slice : (start?: number, end?: number) => any[] +>reverse : () => any[] + + console.log(reversedVal1); +>console.log(reversedVal1) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>reversedVal1 : any[] + + } else { + console.log(val1); +>console.log(val1) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>val1 : string | number | readonly (string | number)[] + } + console.log(val2); +>console.log(val2) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>val2 : readonly (string | number)[] + + }); +} + +testFunc(); +>testFunc() : void +>testFunc : () => void + diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.symbols b/tests/baselines/reference/typeGuardIntersectionTypes.symbols index f99148a9f0500..21da29e434fec 100644 --- a/tests/baselines/reference/typeGuardIntersectionTypes.symbols +++ b/tests/baselines/reference/typeGuardIntersectionTypes.symbols @@ -176,17 +176,17 @@ function identifyBeast(beast: Beast) { >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) if (beast.legs === 4) { ->beast.legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) ->legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) log(`pegasus - 4 legs, wings`); >log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 48, 1)) } else if (beast.legs === 2) { ->beast.legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) ->legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) log(`bird - 2 legs, wings`); >log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 48, 1)) @@ -194,9 +194,9 @@ function identifyBeast(beast: Beast) { else { log(`unknown - ${beast.legs} legs, wings`); >log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 48, 1)) ->beast.legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) ->legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) } } @@ -204,9 +204,9 @@ function identifyBeast(beast: Beast) { else { log(`manbearpig - ${beast.legs} legs, no wings`); >log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 48, 1)) ->beast.legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 64, 23)) ->legs : Symbol(legs, Decl(typeGuardIntersectionTypes.ts, 55, 38), Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 56, 21)) } } diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.types b/tests/baselines/reference/typeGuardIntersectionTypes.types index bf119d5743c6e..a9f43b4917930 100644 --- a/tests/baselines/reference/typeGuardIntersectionTypes.types +++ b/tests/baselines/reference/typeGuardIntersectionTypes.types @@ -166,12 +166,12 @@ function identifyBeast(beast: Beast) { if (hasWings(beast)) { >hasWings(beast) : boolean >hasWings : (x: Beast) => x is Winged ->beast : Beast & Legged +>beast : Legged if (beast.legs === 4) { >beast.legs === 4 : boolean >beast.legs : number ->beast : Beast & Legged & Winged +>beast : Legged & Winged >legs : number >4 : 4 @@ -183,7 +183,7 @@ function identifyBeast(beast: Beast) { else if (beast.legs === 2) { >beast.legs === 2 : boolean >beast.legs : number ->beast : Beast & Legged & Winged +>beast : Legged & Winged >legs : number >2 : 2 @@ -198,7 +198,7 @@ function identifyBeast(beast: Beast) { >log : (s: string) => void >`unknown - ${beast.legs} legs, wings` : string >beast.legs : number ->beast : Beast & Legged & Winged +>beast : Legged & Winged >legs : number } } @@ -210,7 +210,7 @@ function identifyBeast(beast: Beast) { >log : (s: string) => void >`manbearpig - ${beast.legs} legs, no wings` : string >beast.legs : number ->beast : Beast & Legged +>beast : Legged >legs : number } } diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt b/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt index c6f44d4f15274..ee651e82e7d39 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt @@ -1,8 +1,12 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts(7,20): error TS2339: Property 'global' does not exist on type 'never'. The intersection 'I & RegExp' was reduced to 'never' because property 'global' has conflicting types in some constituents. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts(36,11): error TS2339: Property 'onChanges' does not exist on type 'C | (Validator & Partial)'. + Property 'onChanges' does not exist on type 'C'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts(37,11): error TS2339: Property 'onChanges' does not exist on type 'C | (Validator & Partial)'. + Property 'onChanges' does not exist on type 'C'. -==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts (1 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts (3 errors) ==== interface I { global: string; } var result!: I; var result2!: I; @@ -36,8 +40,19 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts(7,20) v // Validator & Partial & C } v // Validator & Partial via subtype reduction + + // In 4.1, we introduced a change which _fixed_ a bug with CFA + // correctly setting this to be the right object. With 4.2, + // we reverted that fix in #42231 which brought behavior back to + // before 4.1. if (v.onChanges) { + ~~~~~~~~~ +!!! error TS2339: Property 'onChanges' does not exist on type 'C | (Validator & Partial)'. +!!! error TS2339: Property 'onChanges' does not exist on type 'C'. v.onChanges({}); + ~~~~~~~~~ +!!! error TS2339: Property 'onChanges' does not exist on type 'C | (Validator & Partial)'. +!!! error TS2339: Property 'onChanges' does not exist on type 'C'. } } diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.js b/tests/baselines/reference/typeGuardsWithInstanceOf.js index 689712ad22e90..7e1af310c738d 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.js +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.js @@ -29,6 +29,11 @@ function foo() { v // Validator & Partial & C } v // Validator & Partial via subtype reduction + + // In 4.1, we introduced a change which _fixed_ a bug with CFA + // correctly setting this to be the right object. With 4.2, + // we reverted that fix in #42231 which brought behavior back to + // before 4.1. if (v.onChanges) { v.onChanges({}); } @@ -58,6 +63,10 @@ function foo() { v; // Validator & Partial & C } v; // Validator & Partial via subtype reduction + // In 4.1, we introduced a change which _fixed_ a bug with CFA + // correctly setting this to be the right object. With 4.2, + // we reverted that fix in #42231 which brought behavior back to + // before 4.1. if (v.onChanges) { v.onChanges({}); } diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.symbols b/tests/baselines/reference/typeGuardsWithInstanceOf.symbols index 018c6bac6da52..eefd3700b6485 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.symbols +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.symbols @@ -70,15 +70,15 @@ function foo() { v // Validator & Partial via subtype reduction >v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7)) + // In 4.1, we introduced a change which _fixed_ a bug with CFA + // correctly setting this to be the right object. With 4.2, + // we reverted that fix in #42231 which brought behavior back to + // before 4.1. if (v.onChanges) { ->v.onChanges : Symbol(onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21)) >v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7)) ->onChanges : Symbol(onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21)) v.onChanges({}); ->v.onChanges : Symbol(onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21)) >v : Symbol(v, Decl(typeGuardsWithInstanceOf.ts, 25, 7)) ->onChanges : Symbol(onChanges, Decl(typeGuardsWithInstanceOf.ts, 11, 21)) } } diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.types b/tests/baselines/reference/typeGuardsWithInstanceOf.types index 198769effc2c4..0b57dbff89bf1 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.types +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.types @@ -65,21 +65,25 @@ function foo() { >C : typeof C v // Validator & Partial & C ->v : Validator & Partial & C +>v : C } v // Validator & Partial via subtype reduction ->v : Validator & Partial +>v : C | (Validator & Partial) + // In 4.1, we introduced a change which _fixed_ a bug with CFA + // correctly setting this to be the right object. With 4.2, + // we reverted that fix in #42231 which brought behavior back to + // before 4.1. if (v.onChanges) { ->v.onChanges : ((changes: Record) => void) | undefined ->v : Validator & Partial ->onChanges : ((changes: Record) => void) | undefined +>v.onChanges : any +>v : C | (Validator & Partial) +>onChanges : any v.onChanges({}); ->v.onChanges({}) : void ->v.onChanges : (changes: Record) => void ->v : Validator & Partial ->onChanges : (changes: Record) => void +>v.onChanges({}) : any +>v.onChanges : any +>v : C | (Validator & Partial) +>onChanges : any >{} : {} } } diff --git a/tests/cases/compiler/narrowingAssignmentReadonlyRespectsAssertion.ts b/tests/cases/compiler/narrowingAssignmentReadonlyRespectsAssertion.ts new file mode 100644 index 0000000000000..adc138d60d466 --- /dev/null +++ b/tests/cases/compiler/narrowingAssignmentReadonlyRespectsAssertion.ts @@ -0,0 +1,39 @@ +// https://github.com/microsoft/TypeScript/issues/41984 + +interface TestCase { + readonly val1: T | ReadonlyArray; + readonly val2: ReadonlyArray; +} + +interface MultiCaseFixture { + cases: T[]; +} + +function subDataFunc(): TestCase[] { + return [ + { val1: "a", val2: ["a", "b", "c"] }, + { val1: 2, val2: [1, 2, 3] }, + { val1: ["a", "z"], val2: ["x", "y", "z"] }, + { val1: [5, 10], val2: [10, 100, 1000] }, + ]; +} + +function dataFunc(subFunc: () => T[]): MultiCaseFixture { + return { cases: subFunc() }; +} + +function testFunc() { + const fixture = dataFunc>(subDataFunc); + fixture.cases.forEach(({ val1, val2 }) => { + if (Array.isArray(val1)) { + // This should retain val1 as being an array + const reversedVal1 = val1.slice().reverse(); + console.log(reversedVal1); + } else { + console.log(val1); + } + console.log(val2); + }); +} + +testFunc(); diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts index ec647d9bb7c9a..a1fee0303f231 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts @@ -30,6 +30,11 @@ function foo() { v // Validator & Partial & C } v // Validator & Partial via subtype reduction + + // In 4.1, we introduced a change which _fixed_ a bug with CFA + // correctly setting this to be the right object. With 4.2, + // we reverted that fix in #42231 which brought behavior back to + // before 4.1. if (v.onChanges) { v.onChanges({}); }