Revert assignability cases in getNarrowedType (#42231)

* Revert subtype narrowing changes from readonly array PRs

* Adds a test for the change

* More baselines
This commit is contained in:
Orta Therox 2021-02-09 23:03:11 +00:00 committed by GitHub
parent 1b19af0f14
commit 8c5fa5cc91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 453 additions and 35 deletions

View File

@ -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 {

View File

@ -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
}
}

View File

@ -0,0 +1,71 @@
//// [narrowingAssignmentReadonlyRespectsAssertion.ts]
// https://github.com/microsoft/TypeScript/issues/41984
interface TestCase<T extends string | number> {
readonly val1: T | ReadonlyArray<T>;
readonly val2: ReadonlyArray<T>;
}
interface MultiCaseFixture<T> {
cases: T[];
}
function subDataFunc(): TestCase<string | number>[] {
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<T>(subFunc: () => T[]): MultiCaseFixture<T> {
return { cases: subFunc() };
}
function testFunc() {
const fixture = dataFunc<TestCase<string | number>>(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();

View File

@ -0,0 +1,123 @@
=== tests/cases/compiler/narrowingAssignmentReadonlyRespectsAssertion.ts ===
// https://github.com/microsoft/TypeScript/issues/41984
interface TestCase<T extends string | number> {
>TestCase : Symbol(TestCase, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 0, 0))
>T : Symbol(T, Decl(narrowingAssignmentReadonlyRespectsAssertion.ts, 2, 19))
readonly val1: T | ReadonlyArray<T>;
>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<T>;
>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<T> {
>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<string | number>[] {
>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<T>(subFunc: () => T[]): MultiCaseFixture<T> {
>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<TestCase<string | number>>(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))

View File

@ -0,0 +1,147 @@
=== tests/cases/compiler/narrowingAssignmentReadonlyRespectsAssertion.ts ===
// https://github.com/microsoft/TypeScript/issues/41984
interface TestCase<T extends string | number> {
readonly val1: T | ReadonlyArray<T>;
>val1 : T | readonly T[]
readonly val2: ReadonlyArray<T>;
>val2 : readonly T[]
}
interface MultiCaseFixture<T> {
cases: T[];
>cases : T[]
}
function subDataFunc(): TestCase<string | number>[] {
>subDataFunc : () => TestCase<string | number>[]
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<T>(subFunc: () => T[]): MultiCaseFixture<T> {
>dataFunc : <T>(subFunc: () => T[]) => MultiCaseFixture<T>
>subFunc : () => T[]
return { cases: subFunc() };
>{ cases: subFunc() } : { cases: T[]; }
>cases : T[]
>subFunc() : T[]
>subFunc : () => T[]
}
function testFunc() {
>testFunc : () => void
const fixture = dataFunc<TestCase<string | number>>(subDataFunc);
>fixture : MultiCaseFixture<TestCase<string | number>>
>dataFunc<TestCase<string | number>>(subDataFunc) : MultiCaseFixture<TestCase<string | number>>
>dataFunc : <T>(subFunc: () => T[]) => MultiCaseFixture<T>
>subDataFunc : () => TestCase<string | number>[]
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<string | number>, index: number, array: TestCase<string | number>[]) => void, thisArg?: any) => void
>fixture.cases : TestCase<string | number>[]
>fixture : MultiCaseFixture<TestCase<string | number>>
>cases : TestCase<string | number>[]
>forEach : (callbackfn: (value: TestCase<string | number>, index: number, array: TestCase<string | number>[]) => 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<string | number>) => 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

View File

@ -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))
}
}

View File

@ -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
}
}

View File

@ -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<OnChanges>)'.
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<OnChanges>)'.
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<OnChanges> & C
}
v // Validator & Partial<OnChanges> 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<OnChanges>)'.
!!! error TS2339: Property 'onChanges' does not exist on type 'C'.
v.onChanges({});
~~~~~~~~~
!!! error TS2339: Property 'onChanges' does not exist on type 'C | (Validator & Partial<OnChanges>)'.
!!! error TS2339: Property 'onChanges' does not exist on type 'C'.
}
}

View File

@ -29,6 +29,11 @@ function foo() {
v // Validator & Partial<OnChanges> & C
}
v // Validator & Partial<OnChanges> 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<OnChanges> & C
}
v; // Validator & Partial<OnChanges> 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({});
}

View File

@ -70,15 +70,15 @@ function foo() {
v // Validator & Partial<OnChanges> 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))
}
}

View File

@ -65,21 +65,25 @@ function foo() {
>C : typeof C
v // Validator & Partial<OnChanges> & C
>v : Validator & Partial<OnChanges> & C
>v : C
}
v // Validator & Partial<OnChanges> via subtype reduction
>v : Validator & Partial<OnChanges>
>v : C | (Validator & Partial<OnChanges>)
// 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<string, unknown>) => void) | undefined
>v : Validator & Partial<OnChanges>
>onChanges : ((changes: Record<string, unknown>) => void) | undefined
>v.onChanges : any
>v : C | (Validator & Partial<OnChanges>)
>onChanges : any
v.onChanges({});
>v.onChanges({}) : void
>v.onChanges : (changes: Record<string, unknown>) => void
>v : Validator & Partial<OnChanges>
>onChanges : (changes: Record<string, unknown>) => void
>v.onChanges({}) : any
>v.onChanges : any
>v : C | (Validator & Partial<OnChanges>)
>onChanges : any
>{} : {}
}
}

View File

@ -0,0 +1,39 @@
// https://github.com/microsoft/TypeScript/issues/41984
interface TestCase<T extends string | number> {
readonly val1: T | ReadonlyArray<T>;
readonly val2: ReadonlyArray<T>;
}
interface MultiCaseFixture<T> {
cases: T[];
}
function subDataFunc(): TestCase<string | number>[] {
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<T>(subFunc: () => T[]): MultiCaseFixture<T> {
return { cases: subFunc() };
}
function testFunc() {
const fixture = dataFunc<TestCase<string | number>>(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();

View File

@ -30,6 +30,11 @@ function foo() {
v // Validator & Partial<OnChanges> & C
}
v // Validator & Partial<OnChanges> 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({});
}