Skip to content

Commit

Permalink
Revert assignability cases in getNarrowedType (#42231)
Browse files Browse the repository at this point in the history
* Revert subtype narrowing changes from readonly array PRs

* Adds a test for the change

* More baselines
  • Loading branch information
Orta Therox committed Feb 9, 2021
1 parent 1b19af0 commit 8c5fa5c
Show file tree
Hide file tree
Showing 13 changed files with 453 additions and 35 deletions.
13 changes: 9 additions & 4 deletions src/compiler/checker.ts
Expand Up @@ -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 {
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/instanceOfAssignability.types
Expand Up @@ -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
}
}

Expand Down Expand Up @@ -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
}
}

Expand Down
@@ -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();
@@ -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))

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

0 comments on commit 8c5fa5c

Please sign in to comment.