From 949daeed7b3fa2dbd6925fed893b8d947b5982a2 Mon Sep 17 00:00:00 2001
From: Wesley Wigham <wewigham@microsoft.com>
Date: Fri, 4 Mar 2022 13:44:05 -0800
Subject: [PATCH] Use strict variance checks for strict subtype checks
 unconditionally

---
 src/compiler/checker.ts                       |  13 ++-
 ...ayLiteralWithMultipleBestCommonTypes.types |   4 +-
 .../reference/arrayOfFunctionTypes3.types     |  14 +--
 .../reference/bestChoiceType.symbols          |  18 +--
 .../baselines/reference/bestChoiceType.types  |  74 ++++++-------
 .../contextualTypingArrayOfLambdas.types      |   4 +-
 .../flatArrayNoExcessiveStackDepth.types      |   6 +-
 ...tializedDestructuringAssignmentTypes.types |   2 +-
 .../reference/strictSubtypeReduction.js       |  61 +++++++++++
 .../reference/strictSubtypeReduction.symbols  |  99 +++++++++++++++++
 .../reference/strictSubtypeReduction.types    | 103 ++++++++++++++++++
 ...trictSubtypeReductionStrictMode.errors.txt |  79 ++++++++++++++
 .../strictSubtypeReductionStrictMode.js       |  62 +++++++++++
 .../strictSubtypeReductionStrictMode.symbols  |  99 +++++++++++++++++
 .../strictSubtypeReductionStrictMode.types    | 103 ++++++++++++++++++
 .../subtypingWithCallSignatures3.types        |  16 +--
 .../subtypingWithConstructSignatures3.types   |  16 +--
 .../reference/tsxUnionElementType3.errors.txt |  12 +-
 .../reference/tsxUnionElementType3.types      |   8 +-
 .../reference/tsxUnionElementType4.errors.txt |   7 +-
 .../reference/tsxUnionElementType4.types      |   6 +-
 .../cases/compiler/strictSubtypeReduction.ts  |  32 ++++++
 .../strictSubtypeReductionStrictMode.ts       |  33 ++++++
 23 files changed, 770 insertions(+), 101 deletions(-)
 create mode 100644 tests/baselines/reference/strictSubtypeReduction.js
 create mode 100644 tests/baselines/reference/strictSubtypeReduction.symbols
 create mode 100644 tests/baselines/reference/strictSubtypeReduction.types
 create mode 100644 tests/baselines/reference/strictSubtypeReductionStrictMode.errors.txt
 create mode 100644 tests/baselines/reference/strictSubtypeReductionStrictMode.js
 create mode 100644 tests/baselines/reference/strictSubtypeReductionStrictMode.symbols
 create mode 100644 tests/baselines/reference/strictSubtypeReductionStrictMode.types
 create mode 100644 tests/cases/compiler/strictSubtypeReduction.ts
 create mode 100644 tests/cases/compiler/strictSubtypeReductionStrictMode.ts

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 9dfb4a7e112c2..cc45e70633c78 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -17681,7 +17681,7 @@ namespace ts {
             target: Signature,
             ignoreReturnTypes: boolean): boolean {
             return compareSignaturesRelated(source, target, ignoreReturnTypes ? SignatureCheckMode.IgnoreReturnTypes : 0, /*reportErrors*/ false,
-                /*errorReporter*/ undefined, /*errorReporter*/ undefined, compareTypesAssignable, /*reportUnreliableMarkers*/ undefined) !== Ternary.False;
+                /*errorReporter*/ undefined, /*errorReporter*/ undefined, compareTypesAssignable, /*reportUnreliableMarkers*/ undefined, assignableRelation) !== Ternary.False;
         }
 
         type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void;
@@ -17705,7 +17705,8 @@ namespace ts {
             errorReporter: ErrorReporter | undefined,
             incompatibleErrorReporter: ((source: Type, target: Type) => void) | undefined,
             compareTypes: TypeComparer,
-            reportUnreliableMarkers: TypeMapper | undefined): Ternary {
+            reportUnreliableMarkers: TypeMapper | undefined,
+            relation: typeof strictSubtypeRelation): Ternary {
             // TODO (drosen): De-duplicate code between related functions.
             if (source === target) {
                 return Ternary.True;
@@ -17735,8 +17736,8 @@ namespace ts {
             }
 
             const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
-            const strictVariance = !(checkMode & SignatureCheckMode.Callback) && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration &&
-                kind !== SyntaxKind.MethodSignature && kind !== SyntaxKind.Constructor;
+            const strictVariance = relation === strictSubtypeRelation || (!(checkMode & SignatureCheckMode.Callback) && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration &&
+                kind !== SyntaxKind.MethodSignature && kind !== SyntaxKind.Constructor);
             let result = Ternary.True;
 
             const sourceThisType = getThisTypeOfSignature(source);
@@ -17776,7 +17777,7 @@ namespace ts {
                     const callbacks = sourceSig && targetSig && !getTypePredicateOfSignature(sourceSig) && !getTypePredicateOfSignature(targetSig) &&
                         (getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable);
                     let related = callbacks ?
-                        compareSignaturesRelated(targetSig, sourceSig, (checkMode & SignatureCheckMode.StrictArity) | (strictVariance ? SignatureCheckMode.StrictCallback : SignatureCheckMode.BivariantCallback), reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) :
+                        compareSignaturesRelated(targetSig, sourceSig, (checkMode & SignatureCheckMode.StrictArity) | (strictVariance ? SignatureCheckMode.StrictCallback : SignatureCheckMode.BivariantCallback), reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers, relation) :
                         !(checkMode & SignatureCheckMode.Callback) && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
                     // With strict arity, (x: number | undefined) => void is a subtype of (x?: number | undefined) => void
                     if (related && checkMode & SignatureCheckMode.StrictArity && i >= getMinArgumentCount(source) && i < getMinArgumentCount(target) && compareTypes(sourceType, targetType, /*reportErrors*/ false)) {
@@ -20173,7 +20174,7 @@ namespace ts {
              */
             function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
                 return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
-                    relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedToWorker, makeFunctionTypeMapper(reportUnreliableMarkers));
+                    relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedToWorker, makeFunctionTypeMapper(reportUnreliableMarkers), relation);
             }
 
             function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
diff --git a/tests/baselines/reference/arrayLiteralWithMultipleBestCommonTypes.types b/tests/baselines/reference/arrayLiteralWithMultipleBestCommonTypes.types
index a7553fd5cd5f0..6572893f90013 100644
--- a/tests/baselines/reference/arrayLiteralWithMultipleBestCommonTypes.types
+++ b/tests/baselines/reference/arrayLiteralWithMultipleBestCommonTypes.types
@@ -36,8 +36,8 @@ var cs = [a, b, c]; // { x: number; y?: number };[]
 >c : { x: number; a?: number; }
 
 var ds = [(x: Object) => 1, (x: string) => 2]; // { (x:Object) => number }[]
->ds : ((x: Object) => number)[]
->[(x: Object) => 1, (x: string) => 2] : ((x: Object) => number)[]
+>ds : ((x: string) => number)[]
+>[(x: Object) => 1, (x: string) => 2] : ((x: string) => number)[]
 >(x: Object) => 1 : (x: Object) => number
 >x : Object
 >1 : 1
diff --git a/tests/baselines/reference/arrayOfFunctionTypes3.types b/tests/baselines/reference/arrayOfFunctionTypes3.types
index 4dd258645a6f2..b4ea3b59ba10a 100644
--- a/tests/baselines/reference/arrayOfFunctionTypes3.types
+++ b/tests/baselines/reference/arrayOfFunctionTypes3.types
@@ -50,28 +50,28 @@ var c: { (x: number): number; (x: any): any; };
 >x : any
 
 var z = [a, b, c];
->z : { (x: number): number; (x: any): any; }[]
->[a, b, c] : { (x: number): number; (x: any): any; }[]
+>z : ({ (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; })[]
+>[a, b, c] : ({ (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; })[]
 >a : { (x: number): number; (x: string): string; }
 >b : { (x: number): number; (x: string): string; }
 >c : { (x: number): number; (x: any): any; }
 
 var r4 = z[0];
->r4 : { (x: number): number; (x: any): any; }
->z[0] : { (x: number): number; (x: any): any; }
->z : { (x: number): number; (x: any): any; }[]
+>r4 : { (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; }
+>z[0] : { (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; }
+>z : ({ (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; })[]
 >0 : 0
 
 var r5 = r4(''); // any not string
 >r5 : any
 >r4('') : any
->r4 : { (x: number): number; (x: any): any; }
+>r4 : { (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; }
 >'' : ""
 
 var r5b = r4(1);
 >r5b : number
 >r4(1) : number
->r4 : { (x: number): number; (x: any): any; }
+>r4 : { (x: number): number; (x: string): string; } | { (x: number): number; (x: any): any; }
 >1 : 1
 
 var a2: { <T>(x: T): number; (x: string): string;};
diff --git a/tests/baselines/reference/bestChoiceType.symbols b/tests/baselines/reference/bestChoiceType.symbols
index c7732890347d0..0072356223368 100644
--- a/tests/baselines/reference/bestChoiceType.symbols
+++ b/tests/baselines/reference/bestChoiceType.symbols
@@ -2,14 +2,12 @@
 // Repro from #10041
 
 (''.match(/ /) || []).map(s => s.toLowerCase());
->(''.match(/ /) || []).map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
+>(''.match(/ /) || []).map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
 >''.match : Symbol(String.match, Decl(lib.es5.d.ts, --, --))
 >match : Symbol(String.match, Decl(lib.es5.d.ts, --, --))
->map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
+>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
 >s : Symbol(s, Decl(bestChoiceType.ts, 2, 26))
->s.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
 >s : Symbol(s, Decl(bestChoiceType.ts, 2, 26))
->toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
 
 // Similar cases
 
@@ -27,13 +25,11 @@ function f1() {
 
     let z = y.map(s => s.toLowerCase());
 >z : Symbol(z, Decl(bestChoiceType.ts, 9, 7))
->y.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
+>y.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
 >y : Symbol(y, Decl(bestChoiceType.ts, 8, 7))
->map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
+>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
 >s : Symbol(s, Decl(bestChoiceType.ts, 9, 18))
->s.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
 >s : Symbol(s, Decl(bestChoiceType.ts, 9, 18))
->toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
 }
 
 function f2() {
@@ -51,12 +47,10 @@ function f2() {
 
     let z = y.map(s => s.toLowerCase());
 >z : Symbol(z, Decl(bestChoiceType.ts, 15, 7))
->y.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
+>y.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
 >y : Symbol(y, Decl(bestChoiceType.ts, 14, 7))
->map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
+>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
 >s : Symbol(s, Decl(bestChoiceType.ts, 15, 18))
->s.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
 >s : Symbol(s, Decl(bestChoiceType.ts, 15, 18))
->toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
 }
 
diff --git a/tests/baselines/reference/bestChoiceType.types b/tests/baselines/reference/bestChoiceType.types
index 49997d1e273cc..95fc6fb0ca5c3 100644
--- a/tests/baselines/reference/bestChoiceType.types
+++ b/tests/baselines/reference/bestChoiceType.types
@@ -2,23 +2,23 @@
 // Repro from #10041
 
 (''.match(/ /) || []).map(s => s.toLowerCase());
->(''.match(/ /) || []).map(s => s.toLowerCase()) : string[]
->(''.match(/ /) || []).map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
->(''.match(/ /) || []) : RegExpMatchArray
->''.match(/ /) || [] : RegExpMatchArray
+>(''.match(/ /) || []).map(s => s.toLowerCase()) : any[]
+>(''.match(/ /) || []).map : (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])
+>(''.match(/ /) || []) : RegExpMatchArray | never[]
+>''.match(/ /) || [] : RegExpMatchArray | never[]
 >''.match(/ /) : RegExpMatchArray | null
 >''.match : (regexp: string | RegExp) => RegExpMatchArray | null
 >'' : ""
 >match : (regexp: string | RegExp) => RegExpMatchArray | null
 >/ / : RegExp
 >[] : never[]
->map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
->s => s.toLowerCase() : (s: string) => string
->s : string
->s.toLowerCase() : string
->s.toLowerCase : () => string
->s : string
->toLowerCase : () => string
+>map : (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])
+>s => s.toLowerCase() : (s: any) => any
+>s : any
+>s.toLowerCase() : any
+>s.toLowerCase : any
+>s : any
+>toLowerCase : any
 
 // Similar cases
 
@@ -34,23 +34,23 @@ function f1() {
 >/ / : RegExp
 
     let y = x || [];
->y : RegExpMatchArray
->x || [] : RegExpMatchArray
+>y : RegExpMatchArray | never[]
+>x || [] : RegExpMatchArray | never[]
 >x : RegExpMatchArray | null
 >[] : never[]
 
     let z = y.map(s => s.toLowerCase());
->z : string[]
->y.map(s => s.toLowerCase()) : string[]
->y.map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
->y : RegExpMatchArray
->map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
->s => s.toLowerCase() : (s: string) => string
->s : string
->s.toLowerCase() : string
->s.toLowerCase : () => string
->s : string
->toLowerCase : () => string
+>z : any[]
+>y.map(s => s.toLowerCase()) : any[]
+>y.map : (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])
+>y : RegExpMatchArray | never[]
+>map : (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])
+>s => s.toLowerCase() : (s: any) => any
+>s : any
+>s.toLowerCase() : any
+>s.toLowerCase : any
+>s : any
+>toLowerCase : any
 }
 
 function f2() {
@@ -65,23 +65,23 @@ function f2() {
 >/ / : RegExp
 
     let y = x ? x : [];
->y : RegExpMatchArray
->x ? x : [] : RegExpMatchArray
+>y : RegExpMatchArray | never[]
+>x ? x : [] : RegExpMatchArray | never[]
 >x : RegExpMatchArray | null
 >x : RegExpMatchArray
 >[] : never[]
 
     let z = y.map(s => s.toLowerCase());
->z : string[]
->y.map(s => s.toLowerCase()) : string[]
->y.map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
->y : RegExpMatchArray
->map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
->s => s.toLowerCase() : (s: string) => string
->s : string
->s.toLowerCase() : string
->s.toLowerCase : () => string
->s : string
->toLowerCase : () => string
+>z : any[]
+>y.map(s => s.toLowerCase()) : any[]
+>y.map : (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])
+>y : RegExpMatchArray | never[]
+>map : (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])
+>s => s.toLowerCase() : (s: any) => any
+>s : any
+>s.toLowerCase() : any
+>s.toLowerCase : any
+>s : any
+>toLowerCase : any
 }
 
diff --git a/tests/baselines/reference/contextualTypingArrayOfLambdas.types b/tests/baselines/reference/contextualTypingArrayOfLambdas.types
index 36b8aaa715f6f..ffe589c092927 100644
--- a/tests/baselines/reference/contextualTypingArrayOfLambdas.types
+++ b/tests/baselines/reference/contextualTypingArrayOfLambdas.types
@@ -23,8 +23,8 @@ class C extends A {
 }
 
 var xs = [(x: A) => { }, (x: B) => { }, (x: C) => { }];
->xs : ((x: A) => void)[]
->[(x: A) => { }, (x: B) => { }, (x: C) => { }] : ((x: A) => void)[]
+>xs : (((x: B) => void) | ((x: C) => void))[]
+>[(x: A) => { }, (x: B) => { }, (x: C) => { }] : (((x: B) => void) | ((x: C) => void))[]
 >(x: A) => { } : (x: A) => void
 >x : A
 >(x: B) => { } : (x: B) => void
diff --git a/tests/baselines/reference/flatArrayNoExcessiveStackDepth.types b/tests/baselines/reference/flatArrayNoExcessiveStackDepth.types
index 51bde63a33b80..ff1d3b3e6939a 100644
--- a/tests/baselines/reference/flatArrayNoExcessiveStackDepth.types
+++ b/tests/baselines/reference/flatArrayNoExcessiveStackDepth.types
@@ -36,8 +36,8 @@ const repro_43249 = (value: unknown) => {
 >"No" : "No"
     }
     const match = value.match(/anything/) || [];
->match : RegExpMatchArray
->value.match(/anything/) || [] : RegExpMatchArray
+>match : RegExpMatchArray | never[]
+>value.match(/anything/) || [] : RegExpMatchArray | never[]
 >value.match(/anything/) : RegExpMatchArray | null
 >value.match : { (regexp: string | RegExp): RegExpMatchArray | null; (matcher: { [Symbol.match](string: string): RegExpMatchArray | null; }): RegExpMatchArray | null; }
 >value : string
@@ -48,7 +48,7 @@ const repro_43249 = (value: unknown) => {
     const [, extracted] = match;
 > : undefined
 >extracted : string
->match : RegExpMatchArray
+>match : RegExpMatchArray | never[]
 
 };
 
diff --git a/tests/baselines/reference/initializedDestructuringAssignmentTypes.types b/tests/baselines/reference/initializedDestructuringAssignmentTypes.types
index 333fc5d92ccd8..07fd73428cf33 100644
--- a/tests/baselines/reference/initializedDestructuringAssignmentTypes.types
+++ b/tests/baselines/reference/initializedDestructuringAssignmentTypes.types
@@ -3,7 +3,7 @@ const [, a = ''] = ''.match('') || [];
 > : undefined
 >a : string
 >'' : ""
->''.match('') || [] : RegExpMatchArray
+>''.match('') || [] : RegExpMatchArray | undefined[]
 >''.match('') : RegExpMatchArray
 >''.match : (regexp: string | RegExp) => RegExpMatchArray
 >'' : ""
diff --git a/tests/baselines/reference/strictSubtypeReduction.js b/tests/baselines/reference/strictSubtypeReduction.js
new file mode 100644
index 0000000000000..c373f2c1f3ab7
--- /dev/null
+++ b/tests/baselines/reference/strictSubtypeReduction.js
@@ -0,0 +1,61 @@
+//// [strictSubtypeReduction.ts]
+// Repro from #41977
+
+class S1 {
+    static f(a: number | string): void { }
+}
+
+class S2 {
+    static f(a: number): void { }
+    static g(a: number): void { }
+}
+
+function f(a: number): void { }
+function g(a: number): void { }
+
+// Declaring the following type aliases should have no effect
+
+type T1 = typeof S2.g;
+type T2 = typeof g;
+
+// All should have type ((a: number) => void)[]
+
+const y1 = [S1.f, f];
+const y2 = [S1.f, g];
+const y3 = [S1.f, S2.f];
+const y4 = [S1.f, S2.g];
+
+// All assignments should be errors in strict mode, but won't be without strict function types on
+
+const x1: ((ctrl: number | string) => void)[] = y1;
+const x2: ((ctrl: number | string) => void)[] = y2;
+const x3: ((ctrl: number | string) => void)[] = y3;
+const x4: ((ctrl: number | string) => void)[] = y4;
+
+//// [strictSubtypeReduction.js]
+// Repro from #41977
+var S1 = /** @class */ (function () {
+    function S1() {
+    }
+    S1.f = function (a) { };
+    return S1;
+}());
+var S2 = /** @class */ (function () {
+    function S2() {
+    }
+    S2.f = function (a) { };
+    S2.g = function (a) { };
+    return S2;
+}());
+function f(a) { }
+function g(a) { }
+// All should have type ((a: number) => void)[]
+var y1 = [S1.f, f];
+var y2 = [S1.f, g];
+var y3 = [S1.f, S2.f];
+var y4 = [S1.f, S2.g];
+// All assignments should be errors in strict mode, but won't be without strict function types on
+var x1 = y1;
+var x2 = y2;
+var x3 = y3;
+var x4 = y4;
diff --git a/tests/baselines/reference/strictSubtypeReduction.symbols b/tests/baselines/reference/strictSubtypeReduction.symbols
new file mode 100644
index 0000000000000..46f2f98267de0
--- /dev/null
+++ b/tests/baselines/reference/strictSubtypeReduction.symbols
@@ -0,0 +1,99 @@
+=== tests/cases/compiler/strictSubtypeReduction.ts ===
+// Repro from #41977
+
+class S1 {
+>S1 : Symbol(S1, Decl(strictSubtypeReduction.ts, 0, 0))
+
+    static f(a: number | string): void { }
+>f : Symbol(S1.f, Decl(strictSubtypeReduction.ts, 2, 10))
+>a : Symbol(a, Decl(strictSubtypeReduction.ts, 3, 13))
+}
+
+class S2 {
+>S2 : Symbol(S2, Decl(strictSubtypeReduction.ts, 4, 1))
+
+    static f(a: number): void { }
+>f : Symbol(S2.f, Decl(strictSubtypeReduction.ts, 6, 10))
+>a : Symbol(a, Decl(strictSubtypeReduction.ts, 7, 13))
+
+    static g(a: number): void { }
+>g : Symbol(S2.g, Decl(strictSubtypeReduction.ts, 7, 33))
+>a : Symbol(a, Decl(strictSubtypeReduction.ts, 8, 13))
+}
+
+function f(a: number): void { }
+>f : Symbol(f, Decl(strictSubtypeReduction.ts, 9, 1))
+>a : Symbol(a, Decl(strictSubtypeReduction.ts, 11, 11))
+
+function g(a: number): void { }
+>g : Symbol(g, Decl(strictSubtypeReduction.ts, 11, 31))
+>a : Symbol(a, Decl(strictSubtypeReduction.ts, 12, 11))
+
+// Declaring the following type aliases should have no effect
+
+type T1 = typeof S2.g;
+>T1 : Symbol(T1, Decl(strictSubtypeReduction.ts, 12, 31))
+>S2.g : Symbol(S2.g, Decl(strictSubtypeReduction.ts, 7, 33))
+>S2 : Symbol(S2, Decl(strictSubtypeReduction.ts, 4, 1))
+>g : Symbol(S2.g, Decl(strictSubtypeReduction.ts, 7, 33))
+
+type T2 = typeof g;
+>T2 : Symbol(T2, Decl(strictSubtypeReduction.ts, 16, 22))
+>g : Symbol(g, Decl(strictSubtypeReduction.ts, 11, 31))
+
+// All should have type ((a: number) => void)[]
+
+const y1 = [S1.f, f];
+>y1 : Symbol(y1, Decl(strictSubtypeReduction.ts, 21, 5))
+>S1.f : Symbol(S1.f, Decl(strictSubtypeReduction.ts, 2, 10))
+>S1 : Symbol(S1, Decl(strictSubtypeReduction.ts, 0, 0))
+>f : Symbol(S1.f, Decl(strictSubtypeReduction.ts, 2, 10))
+>f : Symbol(f, Decl(strictSubtypeReduction.ts, 9, 1))
+
+const y2 = [S1.f, g];
+>y2 : Symbol(y2, Decl(strictSubtypeReduction.ts, 22, 5))
+>S1.f : Symbol(S1.f, Decl(strictSubtypeReduction.ts, 2, 10))
+>S1 : Symbol(S1, Decl(strictSubtypeReduction.ts, 0, 0))
+>f : Symbol(S1.f, Decl(strictSubtypeReduction.ts, 2, 10))
+>g : Symbol(g, Decl(strictSubtypeReduction.ts, 11, 31))
+
+const y3 = [S1.f, S2.f];
+>y3 : Symbol(y3, Decl(strictSubtypeReduction.ts, 23, 5))
+>S1.f : Symbol(S1.f, Decl(strictSubtypeReduction.ts, 2, 10))
+>S1 : Symbol(S1, Decl(strictSubtypeReduction.ts, 0, 0))
+>f : Symbol(S1.f, Decl(strictSubtypeReduction.ts, 2, 10))
+>S2.f : Symbol(S2.f, Decl(strictSubtypeReduction.ts, 6, 10))
+>S2 : Symbol(S2, Decl(strictSubtypeReduction.ts, 4, 1))
+>f : Symbol(S2.f, Decl(strictSubtypeReduction.ts, 6, 10))
+
+const y4 = [S1.f, S2.g];
+>y4 : Symbol(y4, Decl(strictSubtypeReduction.ts, 24, 5))
+>S1.f : Symbol(S1.f, Decl(strictSubtypeReduction.ts, 2, 10))
+>S1 : Symbol(S1, Decl(strictSubtypeReduction.ts, 0, 0))
+>f : Symbol(S1.f, Decl(strictSubtypeReduction.ts, 2, 10))
+>S2.g : Symbol(S2.g, Decl(strictSubtypeReduction.ts, 7, 33))
+>S2 : Symbol(S2, Decl(strictSubtypeReduction.ts, 4, 1))
+>g : Symbol(S2.g, Decl(strictSubtypeReduction.ts, 7, 33))
+
+// All assignments should be errors in strict mode, but won't be without strict function types on
+
+const x1: ((ctrl: number | string) => void)[] = y1;
+>x1 : Symbol(x1, Decl(strictSubtypeReduction.ts, 28, 5))
+>ctrl : Symbol(ctrl, Decl(strictSubtypeReduction.ts, 28, 12))
+>y1 : Symbol(y1, Decl(strictSubtypeReduction.ts, 21, 5))
+
+const x2: ((ctrl: number | string) => void)[] = y2;
+>x2 : Symbol(x2, Decl(strictSubtypeReduction.ts, 29, 5))
+>ctrl : Symbol(ctrl, Decl(strictSubtypeReduction.ts, 29, 12))
+>y2 : Symbol(y2, Decl(strictSubtypeReduction.ts, 22, 5))
+
+const x3: ((ctrl: number | string) => void)[] = y3;
+>x3 : Symbol(x3, Decl(strictSubtypeReduction.ts, 30, 5))
+>ctrl : Symbol(ctrl, Decl(strictSubtypeReduction.ts, 30, 12))
+>y3 : Symbol(y3, Decl(strictSubtypeReduction.ts, 23, 5))
+
+const x4: ((ctrl: number | string) => void)[] = y4;
+>x4 : Symbol(x4, Decl(strictSubtypeReduction.ts, 31, 5))
+>ctrl : Symbol(ctrl, Decl(strictSubtypeReduction.ts, 31, 12))
+>y4 : Symbol(y4, Decl(strictSubtypeReduction.ts, 24, 5))
+
diff --git a/tests/baselines/reference/strictSubtypeReduction.types b/tests/baselines/reference/strictSubtypeReduction.types
new file mode 100644
index 0000000000000..aac67307d3710
--- /dev/null
+++ b/tests/baselines/reference/strictSubtypeReduction.types
@@ -0,0 +1,103 @@
+=== tests/cases/compiler/strictSubtypeReduction.ts ===
+// Repro from #41977
+
+class S1 {
+>S1 : S1
+
+    static f(a: number | string): void { }
+>f : (a: number | string) => void
+>a : string | number
+}
+
+class S2 {
+>S2 : S2
+
+    static f(a: number): void { }
+>f : (a: number) => void
+>a : number
+
+    static g(a: number): void { }
+>g : (a: number) => void
+>a : number
+}
+
+function f(a: number): void { }
+>f : (a: number) => void
+>a : number
+
+function g(a: number): void { }
+>g : (a: number) => void
+>a : number
+
+// Declaring the following type aliases should have no effect
+
+type T1 = typeof S2.g;
+>T1 : (a: number) => void
+>S2.g : (a: number) => void
+>S2 : typeof S2
+>g : (a: number) => void
+
+type T2 = typeof g;
+>T2 : (a: number) => void
+>g : (a: number) => void
+
+// All should have type ((a: number) => void)[]
+
+const y1 = [S1.f, f];
+>y1 : ((a: number) => void)[]
+>[S1.f, f] : ((a: number) => void)[]
+>S1.f : (a: string | number) => void
+>S1 : typeof S1
+>f : (a: string | number) => void
+>f : (a: number) => void
+
+const y2 = [S1.f, g];
+>y2 : ((a: number) => void)[]
+>[S1.f, g] : ((a: number) => void)[]
+>S1.f : (a: string | number) => void
+>S1 : typeof S1
+>f : (a: string | number) => void
+>g : (a: number) => void
+
+const y3 = [S1.f, S2.f];
+>y3 : ((a: number) => void)[]
+>[S1.f, S2.f] : ((a: number) => void)[]
+>S1.f : (a: string | number) => void
+>S1 : typeof S1
+>f : (a: string | number) => void
+>S2.f : (a: number) => void
+>S2 : typeof S2
+>f : (a: number) => void
+
+const y4 = [S1.f, S2.g];
+>y4 : ((a: number) => void)[]
+>[S1.f, S2.g] : ((a: number) => void)[]
+>S1.f : (a: string | number) => void
+>S1 : typeof S1
+>f : (a: string | number) => void
+>S2.g : (a: number) => void
+>S2 : typeof S2
+>g : (a: number) => void
+
+// All assignments should be errors in strict mode, but won't be without strict function types on
+
+const x1: ((ctrl: number | string) => void)[] = y1;
+>x1 : ((ctrl: number | string) => void)[]
+>ctrl : string | number
+>y1 : ((a: number) => void)[]
+
+const x2: ((ctrl: number | string) => void)[] = y2;
+>x2 : ((ctrl: number | string) => void)[]
+>ctrl : string | number
+>y2 : ((a: number) => void)[]
+
+const x3: ((ctrl: number | string) => void)[] = y3;
+>x3 : ((ctrl: number | string) => void)[]
+>ctrl : string | number
+>y3 : ((a: number) => void)[]
+
+const x4: ((ctrl: number | string) => void)[] = y4;
+>x4 : ((ctrl: number | string) => void)[]
+>ctrl : string | number
+>y4 : ((a: number) => void)[]
+
diff --git a/tests/baselines/reference/strictSubtypeReductionStrictMode.errors.txt b/tests/baselines/reference/strictSubtypeReductionStrictMode.errors.txt
new file mode 100644
index 0000000000000..077d76d3e3b8e
--- /dev/null
+++ b/tests/baselines/reference/strictSubtypeReductionStrictMode.errors.txt
@@ -0,0 +1,79 @@
+tests/cases/compiler/strictSubtypeReductionStrictMode.ts(29,7): error TS2322: Type '((a: number) => void)[]' is not assignable to type '((ctrl: string | number) => void)[]'.
+  Type '(a: number) => void' is not assignable to type '(ctrl: string | number) => void'.
+    Types of parameters 'a' and 'ctrl' are incompatible.
+      Type 'string | number' is not assignable to type 'number'.
+        Type 'string' is not assignable to type 'number'.
+tests/cases/compiler/strictSubtypeReductionStrictMode.ts(30,7): error TS2322: Type '((a: number) => void)[]' is not assignable to type '((ctrl: string | number) => void)[]'.
+  Type '(a: number) => void' is not assignable to type '(ctrl: string | number) => void'.
+    Types of parameters 'a' and 'ctrl' are incompatible.
+      Type 'string | number' is not assignable to type 'number'.
+        Type 'string' is not assignable to type 'number'.
+tests/cases/compiler/strictSubtypeReductionStrictMode.ts(31,7): error TS2322: Type '((a: number) => void)[]' is not assignable to type '((ctrl: string | number) => void)[]'.
+  Type '(a: number) => void' is not assignable to type '(ctrl: string | number) => void'.
+    Types of parameters 'a' and 'ctrl' are incompatible.
+      Type 'string | number' is not assignable to type 'number'.
+        Type 'string' is not assignable to type 'number'.
+tests/cases/compiler/strictSubtypeReductionStrictMode.ts(32,7): error TS2322: Type '((a: number) => void)[]' is not assignable to type '((ctrl: string | number) => void)[]'.
+  Type '(a: number) => void' is not assignable to type '(ctrl: string | number) => void'.
+    Types of parameters 'a' and 'ctrl' are incompatible.
+      Type 'string | number' is not assignable to type 'number'.
+        Type 'string' is not assignable to type 'number'.
+
+
+==== tests/cases/compiler/strictSubtypeReductionStrictMode.ts (4 errors) ====
+    // Repro from #41977
+    
+    class S1 {
+        static f(a: number | string): void { }
+    }
+    
+    class S2 {
+        static f(a: number): void { }
+        static g(a: number): void { }
+    }
+    
+    function f(a: number): void { }
+    function g(a: number): void { }
+    
+    // Declaring the following type aliases should have no effect
+    
+    type T1 = typeof S2.g;
+    type T2 = typeof g;
+    
+    // All should have type ((a: number) => void)[]
+    
+    const y1 = [S1.f, f];
+    const y2 = [S1.f, g];
+    const y3 = [S1.f, S2.f];
+    const y4 = [S1.f, S2.g];
+    
+    // All assignments should be errors
+    
+    const x1: ((ctrl: number | string) => void)[] = y1;
+          ~~
+!!! error TS2322: Type '((a: number) => void)[]' is not assignable to type '((ctrl: string | number) => void)[]'.
+!!! error TS2322:   Type '(a: number) => void' is not assignable to type '(ctrl: string | number) => void'.
+!!! error TS2322:     Types of parameters 'a' and 'ctrl' are incompatible.
+!!! error TS2322:       Type 'string | number' is not assignable to type 'number'.
+!!! error TS2322:         Type 'string' is not assignable to type 'number'.
+    const x2: ((ctrl: number | string) => void)[] = y2;
+          ~~
+!!! error TS2322: Type '((a: number) => void)[]' is not assignable to type '((ctrl: string | number) => void)[]'.
+!!! error TS2322:   Type '(a: number) => void' is not assignable to type '(ctrl: string | number) => void'.
+!!! error TS2322:     Types of parameters 'a' and 'ctrl' are incompatible.
+!!! error TS2322:       Type 'string | number' is not assignable to type 'number'.
+!!! error TS2322:         Type 'string' is not assignable to type 'number'.
+    const x3: ((ctrl: number | string) => void)[] = y3;
+          ~~
+!!! error TS2322: Type '((a: number) => void)[]' is not assignable to type '((ctrl: string | number) => void)[]'.
+!!! error TS2322:   Type '(a: number) => void' is not assignable to type '(ctrl: string | number) => void'.
+!!! error TS2322:     Types of parameters 'a' and 'ctrl' are incompatible.
+!!! error TS2322:       Type 'string | number' is not assignable to type 'number'.
+!!! error TS2322:         Type 'string' is not assignable to type 'number'.
+    const x4: ((ctrl: number | string) => void)[] = y4;
+          ~~
+!!! error TS2322: Type '((a: number) => void)[]' is not assignable to type '((ctrl: string | number) => void)[]'.
+!!! error TS2322:   Type '(a: number) => void' is not assignable to type '(ctrl: string | number) => void'.
+!!! error TS2322:     Types of parameters 'a' and 'ctrl' are incompatible.
+!!! error TS2322:       Type 'string | number' is not assignable to type 'number'.
+!!! error TS2322:         Type 'string' is not assignable to type 'number'.
\ No newline at end of file
diff --git a/tests/baselines/reference/strictSubtypeReductionStrictMode.js b/tests/baselines/reference/strictSubtypeReductionStrictMode.js
new file mode 100644
index 0000000000000..5e5d520251cb0
--- /dev/null
+++ b/tests/baselines/reference/strictSubtypeReductionStrictMode.js
@@ -0,0 +1,62 @@
+//// [strictSubtypeReductionStrictMode.ts]
+// Repro from #41977
+
+class S1 {
+    static f(a: number | string): void { }
+}
+
+class S2 {
+    static f(a: number): void { }
+    static g(a: number): void { }
+}
+
+function f(a: number): void { }
+function g(a: number): void { }
+
+// Declaring the following type aliases should have no effect
+
+type T1 = typeof S2.g;
+type T2 = typeof g;
+
+// All should have type ((a: number) => void)[]
+
+const y1 = [S1.f, f];
+const y2 = [S1.f, g];
+const y3 = [S1.f, S2.f];
+const y4 = [S1.f, S2.g];
+
+// All assignments should be errors
+
+const x1: ((ctrl: number | string) => void)[] = y1;
+const x2: ((ctrl: number | string) => void)[] = y2;
+const x3: ((ctrl: number | string) => void)[] = y3;
+const x4: ((ctrl: number | string) => void)[] = y4;
+
+//// [strictSubtypeReductionStrictMode.js]
+"use strict";
+// Repro from #41977
+var S1 = /** @class */ (function () {
+    function S1() {
+    }
+    S1.f = function (a) { };
+    return S1;
+}());
+var S2 = /** @class */ (function () {
+    function S2() {
+    }
+    S2.f = function (a) { };
+    S2.g = function (a) { };
+    return S2;
+}());
+function f(a) { }
+function g(a) { }
+// All should have type ((a: number) => void)[]
+var y1 = [S1.f, f];
+var y2 = [S1.f, g];
+var y3 = [S1.f, S2.f];
+var y4 = [S1.f, S2.g];
+// All assignments should be errors
+var x1 = y1;
+var x2 = y2;
+var x3 = y3;
+var x4 = y4;
diff --git a/tests/baselines/reference/strictSubtypeReductionStrictMode.symbols b/tests/baselines/reference/strictSubtypeReductionStrictMode.symbols
new file mode 100644
index 0000000000000..95ad6cff755ae
--- /dev/null
+++ b/tests/baselines/reference/strictSubtypeReductionStrictMode.symbols
@@ -0,0 +1,99 @@
+=== tests/cases/compiler/strictSubtypeReductionStrictMode.ts ===
+// Repro from #41977
+
+class S1 {
+>S1 : Symbol(S1, Decl(strictSubtypeReductionStrictMode.ts, 0, 0))
+
+    static f(a: number | string): void { }
+>f : Symbol(S1.f, Decl(strictSubtypeReductionStrictMode.ts, 2, 10))
+>a : Symbol(a, Decl(strictSubtypeReductionStrictMode.ts, 3, 13))
+}
+
+class S2 {
+>S2 : Symbol(S2, Decl(strictSubtypeReductionStrictMode.ts, 4, 1))
+
+    static f(a: number): void { }
+>f : Symbol(S2.f, Decl(strictSubtypeReductionStrictMode.ts, 6, 10))
+>a : Symbol(a, Decl(strictSubtypeReductionStrictMode.ts, 7, 13))
+
+    static g(a: number): void { }
+>g : Symbol(S2.g, Decl(strictSubtypeReductionStrictMode.ts, 7, 33))
+>a : Symbol(a, Decl(strictSubtypeReductionStrictMode.ts, 8, 13))
+}
+
+function f(a: number): void { }
+>f : Symbol(f, Decl(strictSubtypeReductionStrictMode.ts, 9, 1))
+>a : Symbol(a, Decl(strictSubtypeReductionStrictMode.ts, 11, 11))
+
+function g(a: number): void { }
+>g : Symbol(g, Decl(strictSubtypeReductionStrictMode.ts, 11, 31))
+>a : Symbol(a, Decl(strictSubtypeReductionStrictMode.ts, 12, 11))
+
+// Declaring the following type aliases should have no effect
+
+type T1 = typeof S2.g;
+>T1 : Symbol(T1, Decl(strictSubtypeReductionStrictMode.ts, 12, 31))
+>S2.g : Symbol(S2.g, Decl(strictSubtypeReductionStrictMode.ts, 7, 33))
+>S2 : Symbol(S2, Decl(strictSubtypeReductionStrictMode.ts, 4, 1))
+>g : Symbol(S2.g, Decl(strictSubtypeReductionStrictMode.ts, 7, 33))
+
+type T2 = typeof g;
+>T2 : Symbol(T2, Decl(strictSubtypeReductionStrictMode.ts, 16, 22))
+>g : Symbol(g, Decl(strictSubtypeReductionStrictMode.ts, 11, 31))
+
+// All should have type ((a: number) => void)[]
+
+const y1 = [S1.f, f];
+>y1 : Symbol(y1, Decl(strictSubtypeReductionStrictMode.ts, 21, 5))
+>S1.f : Symbol(S1.f, Decl(strictSubtypeReductionStrictMode.ts, 2, 10))
+>S1 : Symbol(S1, Decl(strictSubtypeReductionStrictMode.ts, 0, 0))
+>f : Symbol(S1.f, Decl(strictSubtypeReductionStrictMode.ts, 2, 10))
+>f : Symbol(f, Decl(strictSubtypeReductionStrictMode.ts, 9, 1))
+
+const y2 = [S1.f, g];
+>y2 : Symbol(y2, Decl(strictSubtypeReductionStrictMode.ts, 22, 5))
+>S1.f : Symbol(S1.f, Decl(strictSubtypeReductionStrictMode.ts, 2, 10))
+>S1 : Symbol(S1, Decl(strictSubtypeReductionStrictMode.ts, 0, 0))
+>f : Symbol(S1.f, Decl(strictSubtypeReductionStrictMode.ts, 2, 10))
+>g : Symbol(g, Decl(strictSubtypeReductionStrictMode.ts, 11, 31))
+
+const y3 = [S1.f, S2.f];
+>y3 : Symbol(y3, Decl(strictSubtypeReductionStrictMode.ts, 23, 5))
+>S1.f : Symbol(S1.f, Decl(strictSubtypeReductionStrictMode.ts, 2, 10))
+>S1 : Symbol(S1, Decl(strictSubtypeReductionStrictMode.ts, 0, 0))
+>f : Symbol(S1.f, Decl(strictSubtypeReductionStrictMode.ts, 2, 10))
+>S2.f : Symbol(S2.f, Decl(strictSubtypeReductionStrictMode.ts, 6, 10))
+>S2 : Symbol(S2, Decl(strictSubtypeReductionStrictMode.ts, 4, 1))
+>f : Symbol(S2.f, Decl(strictSubtypeReductionStrictMode.ts, 6, 10))
+
+const y4 = [S1.f, S2.g];
+>y4 : Symbol(y4, Decl(strictSubtypeReductionStrictMode.ts, 24, 5))
+>S1.f : Symbol(S1.f, Decl(strictSubtypeReductionStrictMode.ts, 2, 10))
+>S1 : Symbol(S1, Decl(strictSubtypeReductionStrictMode.ts, 0, 0))
+>f : Symbol(S1.f, Decl(strictSubtypeReductionStrictMode.ts, 2, 10))
+>S2.g : Symbol(S2.g, Decl(strictSubtypeReductionStrictMode.ts, 7, 33))
+>S2 : Symbol(S2, Decl(strictSubtypeReductionStrictMode.ts, 4, 1))
+>g : Symbol(S2.g, Decl(strictSubtypeReductionStrictMode.ts, 7, 33))
+
+// All assignments should be errors
+
+const x1: ((ctrl: number | string) => void)[] = y1;
+>x1 : Symbol(x1, Decl(strictSubtypeReductionStrictMode.ts, 28, 5))
+>ctrl : Symbol(ctrl, Decl(strictSubtypeReductionStrictMode.ts, 28, 12))
+>y1 : Symbol(y1, Decl(strictSubtypeReductionStrictMode.ts, 21, 5))
+
+const x2: ((ctrl: number | string) => void)[] = y2;
+>x2 : Symbol(x2, Decl(strictSubtypeReductionStrictMode.ts, 29, 5))
+>ctrl : Symbol(ctrl, Decl(strictSubtypeReductionStrictMode.ts, 29, 12))
+>y2 : Symbol(y2, Decl(strictSubtypeReductionStrictMode.ts, 22, 5))
+
+const x3: ((ctrl: number | string) => void)[] = y3;
+>x3 : Symbol(x3, Decl(strictSubtypeReductionStrictMode.ts, 30, 5))
+>ctrl : Symbol(ctrl, Decl(strictSubtypeReductionStrictMode.ts, 30, 12))
+>y3 : Symbol(y3, Decl(strictSubtypeReductionStrictMode.ts, 23, 5))
+
+const x4: ((ctrl: number | string) => void)[] = y4;
+>x4 : Symbol(x4, Decl(strictSubtypeReductionStrictMode.ts, 31, 5))
+>ctrl : Symbol(ctrl, Decl(strictSubtypeReductionStrictMode.ts, 31, 12))
+>y4 : Symbol(y4, Decl(strictSubtypeReductionStrictMode.ts, 24, 5))
+
diff --git a/tests/baselines/reference/strictSubtypeReductionStrictMode.types b/tests/baselines/reference/strictSubtypeReductionStrictMode.types
new file mode 100644
index 0000000000000..dbd29a78efe62
--- /dev/null
+++ b/tests/baselines/reference/strictSubtypeReductionStrictMode.types
@@ -0,0 +1,103 @@
+=== tests/cases/compiler/strictSubtypeReductionStrictMode.ts ===
+// Repro from #41977
+
+class S1 {
+>S1 : S1
+
+    static f(a: number | string): void { }
+>f : (a: number | string) => void
+>a : string | number
+}
+
+class S2 {
+>S2 : S2
+
+    static f(a: number): void { }
+>f : (a: number) => void
+>a : number
+
+    static g(a: number): void { }
+>g : (a: number) => void
+>a : number
+}
+
+function f(a: number): void { }
+>f : (a: number) => void
+>a : number
+
+function g(a: number): void { }
+>g : (a: number) => void
+>a : number
+
+// Declaring the following type aliases should have no effect
+
+type T1 = typeof S2.g;
+>T1 : (a: number) => void
+>S2.g : (a: number) => void
+>S2 : typeof S2
+>g : (a: number) => void
+
+type T2 = typeof g;
+>T2 : (a: number) => void
+>g : (a: number) => void
+
+// All should have type ((a: number) => void)[]
+
+const y1 = [S1.f, f];
+>y1 : ((a: number) => void)[]
+>[S1.f, f] : ((a: number) => void)[]
+>S1.f : (a: string | number) => void
+>S1 : typeof S1
+>f : (a: string | number) => void
+>f : (a: number) => void
+
+const y2 = [S1.f, g];
+>y2 : ((a: number) => void)[]
+>[S1.f, g] : ((a: number) => void)[]
+>S1.f : (a: string | number) => void
+>S1 : typeof S1
+>f : (a: string | number) => void
+>g : (a: number) => void
+
+const y3 = [S1.f, S2.f];
+>y3 : ((a: number) => void)[]
+>[S1.f, S2.f] : ((a: number) => void)[]
+>S1.f : (a: string | number) => void
+>S1 : typeof S1
+>f : (a: string | number) => void
+>S2.f : (a: number) => void
+>S2 : typeof S2
+>f : (a: number) => void
+
+const y4 = [S1.f, S2.g];
+>y4 : ((a: number) => void)[]
+>[S1.f, S2.g] : ((a: number) => void)[]
+>S1.f : (a: string | number) => void
+>S1 : typeof S1
+>f : (a: string | number) => void
+>S2.g : (a: number) => void
+>S2 : typeof S2
+>g : (a: number) => void
+
+// All assignments should be errors
+
+const x1: ((ctrl: number | string) => void)[] = y1;
+>x1 : ((ctrl: number | string) => void)[]
+>ctrl : string | number
+>y1 : ((a: number) => void)[]
+
+const x2: ((ctrl: number | string) => void)[] = y2;
+>x2 : ((ctrl: number | string) => void)[]
+>ctrl : string | number
+>y2 : ((a: number) => void)[]
+
+const x3: ((ctrl: number | string) => void)[] = y3;
+>x3 : ((ctrl: number | string) => void)[]
+>ctrl : string | number
+>y3 : ((a: number) => void)[]
+
+const x4: ((ctrl: number | string) => void)[] = y4;
+>x4 : ((ctrl: number | string) => void)[]
+>ctrl : string | number
+>y4 : ((a: number) => void)[]
+
diff --git a/tests/baselines/reference/subtypingWithCallSignatures3.types b/tests/baselines/reference/subtypingWithCallSignatures3.types
index 7116d132b7d2b..52310f1cfa573 100644
--- a/tests/baselines/reference/subtypingWithCallSignatures3.types
+++ b/tests/baselines/reference/subtypingWithCallSignatures3.types
@@ -305,14 +305,14 @@ module Errors {
 >r4arg : <T extends Derived>(...x: T[]) => T
 
     var r4a = [r4arg2, r4arg];
->r4a : ((...x: Base[]) => Base)[]
->[r4arg2, r4arg] : ((...x: Base[]) => Base)[]
+>r4a : ((<T extends Derived>(...x: T[]) => T) | ((...x: Base[]) => Base))[]
+>[r4arg2, r4arg] : ((<T extends Derived>(...x: T[]) => T) | ((...x: Base[]) => Base))[]
 >r4arg2 : (...x: Base[]) => Base
 >r4arg : <T extends Derived>(...x: T[]) => T
 
     var r4b = [r4arg, r4arg2];
->r4b : ((...x: Base[]) => Base)[]
->[r4arg, r4arg2] : ((...x: Base[]) => Base)[]
+>r4b : ((<T extends Derived>(...x: T[]) => T) | ((...x: Base[]) => Base))[]
+>[r4arg, r4arg2] : ((<T extends Derived>(...x: T[]) => T) | ((...x: Base[]) => Base))[]
 >r4arg : <T extends Derived>(...x: T[]) => T
 >r4arg2 : (...x: Base[]) => Base
 
@@ -342,14 +342,14 @@ module Errors {
 >r5arg : <T extends Derived>(x: T, y: T) => T
 
     var r5a = [r5arg2, r5arg];
->r5a : ((x: { foo: string; }, y: { foo: string; bar: string; }) => Base)[]
->[r5arg2, r5arg] : ((x: { foo: string; }, y: { foo: string; bar: string; }) => Base)[]
+>r5a : ((<T extends Derived>(x: T, y: T) => T) | ((x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[]
+>[r5arg2, r5arg] : ((<T extends Derived>(x: T, y: T) => T) | ((x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[]
 >r5arg2 : (x: { foo: string; }, y: { foo: string; bar: string; }) => Base
 >r5arg : <T extends Derived>(x: T, y: T) => T
 
     var r5b = [r5arg, r5arg2];
->r5b : ((x: { foo: string; }, y: { foo: string; bar: string; }) => Base)[]
->[r5arg, r5arg2] : ((x: { foo: string; }, y: { foo: string; bar: string; }) => Base)[]
+>r5b : ((<T extends Derived>(x: T, y: T) => T) | ((x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[]
+>[r5arg, r5arg2] : ((<T extends Derived>(x: T, y: T) => T) | ((x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[]
 >r5arg : <T extends Derived>(x: T, y: T) => T
 >r5arg2 : (x: { foo: string; }, y: { foo: string; bar: string; }) => Base
 
diff --git a/tests/baselines/reference/subtypingWithConstructSignatures3.types b/tests/baselines/reference/subtypingWithConstructSignatures3.types
index 5cf176caa0934..c78033a447c7a 100644
--- a/tests/baselines/reference/subtypingWithConstructSignatures3.types
+++ b/tests/baselines/reference/subtypingWithConstructSignatures3.types
@@ -276,14 +276,14 @@ module Errors {
 >r4arg1 : new <T extends Derived>(...x: T[]) => T
 
     var r4a = [r4arg2, r4arg1];
->r4a : (new (...x: Base[]) => Base)[]
->[r4arg2, r4arg1] : (new (...x: Base[]) => Base)[]
+>r4a : ((new <T extends Derived>(...x: T[]) => T) | (new (...x: Base[]) => Base))[]
+>[r4arg2, r4arg1] : ((new <T extends Derived>(...x: T[]) => T) | (new (...x: Base[]) => Base))[]
 >r4arg2 : new (...x: Base[]) => Base
 >r4arg1 : new <T extends Derived>(...x: T[]) => T
 
     var r4b = [r4arg1, r4arg2];
->r4b : (new (...x: Base[]) => Base)[]
->[r4arg1, r4arg2] : (new (...x: Base[]) => Base)[]
+>r4b : ((new <T extends Derived>(...x: T[]) => T) | (new (...x: Base[]) => Base))[]
+>[r4arg1, r4arg2] : ((new <T extends Derived>(...x: T[]) => T) | (new (...x: Base[]) => Base))[]
 >r4arg1 : new <T extends Derived>(...x: T[]) => T
 >r4arg2 : new (...x: Base[]) => Base
 
@@ -307,14 +307,14 @@ module Errors {
 >r5arg1 : new <T extends Derived>(x: T, y: T) => T
 
     var r5a = [r5arg2, r5arg1];
->r5a : (new (x: { foo: string; }, y: { foo: string; bar: string; }) => Base)[]
->[r5arg2, r5arg1] : (new (x: { foo: string; }, y: { foo: string; bar: string; }) => Base)[]
+>r5a : ((new <T extends Derived>(x: T, y: T) => T) | (new (x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[]
+>[r5arg2, r5arg1] : ((new <T extends Derived>(x: T, y: T) => T) | (new (x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[]
 >r5arg2 : new (x: { foo: string; }, y: { foo: string; bar: string; }) => Base
 >r5arg1 : new <T extends Derived>(x: T, y: T) => T
 
     var r5b = [r5arg1, r5arg2];
->r5b : (new (x: { foo: string; }, y: { foo: string; bar: string; }) => Base)[]
->[r5arg1, r5arg2] : (new (x: { foo: string; }, y: { foo: string; bar: string; }) => Base)[]
+>r5b : ((new <T extends Derived>(x: T, y: T) => T) | (new (x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[]
+>[r5arg1, r5arg2] : ((new <T extends Derived>(x: T, y: T) => T) | (new (x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[]
 >r5arg1 : new <T extends Derived>(x: T, y: T) => T
 >r5arg2 : new (x: { foo: string; }, y: { foo: string; bar: string; }) => Base
 
diff --git a/tests/baselines/reference/tsxUnionElementType3.errors.txt b/tests/baselines/reference/tsxUnionElementType3.errors.txt
index 73d8ccb0ce2b2..e61786f2c04b2 100644
--- a/tests/baselines/reference/tsxUnionElementType3.errors.txt
+++ b/tests/baselines/reference/tsxUnionElementType3.errors.txt
@@ -1,7 +1,9 @@
 tests/cases/conformance/jsx/file.tsx(32,17): error TS2322: Type 'string' is not assignable to type 'never'.
+tests/cases/conformance/jsx/file.tsx(35,10): error TS2741: Property 'x' is missing in type '{}' but required in type '{ x: number; }'.
+tests/cases/conformance/jsx/file.tsx(36,10): error TS2741: Property 'x' is missing in type '{ "data-extra": string; }' but required in type '{ x: number; }'.
 
 
-==== tests/cases/conformance/jsx/file.tsx (1 errors) ====
+==== tests/cases/conformance/jsx/file.tsx (3 errors) ====
     import React = require('react');
     
     class RC1 extends React.Component<{x : number}, {}> {
@@ -40,4 +42,10 @@ tests/cases/conformance/jsx/file.tsx(32,17): error TS2322: Type 'string' is not
     let a1 = <EmptyRCComp />;
     let a2 = <EmptyRCComp data-prop="hello" />;
     let b = <PartRCComp />
-    let c = <PartRCComp data-extra="hello" />
\ No newline at end of file
+             ~~~~~~~~~~
+!!! error TS2741: Property 'x' is missing in type '{}' but required in type '{ x: number; }'.
+!!! related TS2728 tests/cases/conformance/jsx/file.tsx:3:36: 'x' is declared here.
+    let c = <PartRCComp data-extra="hello" />
+             ~~~~~~~~~~
+!!! error TS2741: Property 'x' is missing in type '{ "data-extra": string; }' but required in type '{ x: number; }'.
+!!! related TS2728 tests/cases/conformance/jsx/file.tsx:3:36: 'x' is declared here.
\ No newline at end of file
diff --git a/tests/baselines/reference/tsxUnionElementType3.types b/tests/baselines/reference/tsxUnionElementType3.types
index 5a797ea483b59..65a7ca0d25eeb 100644
--- a/tests/baselines/reference/tsxUnionElementType3.types
+++ b/tests/baselines/reference/tsxUnionElementType3.types
@@ -69,8 +69,8 @@ var EmptyRCComp = RC3 || RC4;
 >RC4 : typeof RC4
 
 var PartRCComp = RC1 || RC4;
->PartRCComp : typeof RC4
->RC1 || RC4 : typeof RC4
+>PartRCComp : typeof RC1 | typeof RC4
+>RC1 || RC4 : typeof RC1 | typeof RC4
 >RC1 : typeof RC1
 >RC4 : typeof RC4
 
@@ -101,11 +101,11 @@ let a2 = <EmptyRCComp data-prop="hello" />;
 let b = <PartRCComp />
 >b : JSX.Element
 ><PartRCComp /> : JSX.Element
->PartRCComp : typeof RC4
+>PartRCComp : typeof RC1 | typeof RC4
 
 let c = <PartRCComp data-extra="hello" />
 >c : JSX.Element
 ><PartRCComp data-extra="hello" /> : JSX.Element
->PartRCComp : typeof RC4
+>PartRCComp : typeof RC1 | typeof RC4
 >data-extra : string
 
diff --git a/tests/baselines/reference/tsxUnionElementType4.errors.txt b/tests/baselines/reference/tsxUnionElementType4.errors.txt
index 03cb6c62a526e..665af28a91068 100644
--- a/tests/baselines/reference/tsxUnionElementType4.errors.txt
+++ b/tests/baselines/reference/tsxUnionElementType4.errors.txt
@@ -1,11 +1,9 @@
 tests/cases/conformance/jsx/file.tsx(32,17): error TS2322: Type 'boolean' is not assignable to type 'never'.
-tests/cases/conformance/jsx/file.tsx(33,21): error TS2322: Type '{ x: number; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<RC4> & { children?: ReactNode; }'.
-  Property 'x' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<RC4> & { children?: ReactNode; }'.
 tests/cases/conformance/jsx/file.tsx(34,22): error TS2322: Type '{ prop: true; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<RC3> & { children?: ReactNode; }'.
   Property 'prop' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<RC3> & { children?: ReactNode; }'.
 
 
-==== tests/cases/conformance/jsx/file.tsx (3 errors) ====
+==== tests/cases/conformance/jsx/file.tsx (2 errors) ====
     import React = require('react');
     
     class RC1 extends React.Component<{x : number}, {}> {
@@ -42,9 +40,6 @@ tests/cases/conformance/jsx/file.tsx(34,22): error TS2322: Type '{ prop: true; }
 !!! error TS2322: Type 'boolean' is not assignable to type 'never'.
 !!! related TS6500 tests/cases/conformance/jsx/file.tsx:3:36: The expected type comes from property 'x' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<RC1 | RC2> & { x: number; } & { children?: ReactNode; } & { x: string; } & { children?: ReactNode; }'
     let b = <PartRCComp x={10} />
-                        ~
-!!! error TS2322: Type '{ x: number; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<RC4> & { children?: ReactNode; }'.
-!!! error TS2322:   Property 'x' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<RC4> & { children?: ReactNode; }'.
     let c = <EmptyRCComp prop />;
                          ~~~~
 !!! error TS2322: Type '{ prop: true; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<RC3> & { children?: ReactNode; }'.
diff --git a/tests/baselines/reference/tsxUnionElementType4.types b/tests/baselines/reference/tsxUnionElementType4.types
index c0961eb6d1644..d346b144a49e3 100644
--- a/tests/baselines/reference/tsxUnionElementType4.types
+++ b/tests/baselines/reference/tsxUnionElementType4.types
@@ -75,8 +75,8 @@ var EmptyRCComp = RC3 || RC4;
 >RC4 : typeof RC4
 
 var PartRCComp = RC1 || RC4;
->PartRCComp : typeof RC4
->RC1 || RC4 : typeof RC4
+>PartRCComp : typeof RC1 | typeof RC4
+>RC1 || RC4 : typeof RC1 | typeof RC4
 >RC1 : typeof RC1
 >RC4 : typeof RC4
 
@@ -90,7 +90,7 @@ let a = <RCComp x />;
 let b = <PartRCComp x={10} />
 >b : JSX.Element
 ><PartRCComp x={10} /> : JSX.Element
->PartRCComp : typeof RC4
+>PartRCComp : typeof RC1 | typeof RC4
 >x : number
 >10 : 10
 
diff --git a/tests/cases/compiler/strictSubtypeReduction.ts b/tests/cases/compiler/strictSubtypeReduction.ts
new file mode 100644
index 0000000000000..b1dd3f18c8777
--- /dev/null
+++ b/tests/cases/compiler/strictSubtypeReduction.ts
@@ -0,0 +1,32 @@
+// Repro from #41977
+
+class S1 {
+    static f(a: number | string): void { }
+}
+
+class S2 {
+    static f(a: number): void { }
+    static g(a: number): void { }
+}
+
+function f(a: number): void { }
+function g(a: number): void { }
+
+// Declaring the following type aliases should have no effect
+
+type T1 = typeof S2.g;
+type T2 = typeof g;
+
+// All should have type ((a: number) => void)[]
+
+const y1 = [S1.f, f];
+const y2 = [S1.f, g];
+const y3 = [S1.f, S2.f];
+const y4 = [S1.f, S2.g];
+
+// All assignments should be errors in strict mode, but won't be without strict function types on
+
+const x1: ((ctrl: number | string) => void)[] = y1;
+const x2: ((ctrl: number | string) => void)[] = y2;
+const x3: ((ctrl: number | string) => void)[] = y3;
+const x4: ((ctrl: number | string) => void)[] = y4;
\ No newline at end of file
diff --git a/tests/cases/compiler/strictSubtypeReductionStrictMode.ts b/tests/cases/compiler/strictSubtypeReductionStrictMode.ts
new file mode 100644
index 0000000000000..26247fb3ededd
--- /dev/null
+++ b/tests/cases/compiler/strictSubtypeReductionStrictMode.ts
@@ -0,0 +1,33 @@
+// @strict: true
+// Repro from #41977
+
+class S1 {
+    static f(a: number | string): void { }
+}
+
+class S2 {
+    static f(a: number): void { }
+    static g(a: number): void { }
+}
+
+function f(a: number): void { }
+function g(a: number): void { }
+
+// Declaring the following type aliases should have no effect
+
+type T1 = typeof S2.g;
+type T2 = typeof g;
+
+// All should have type ((a: number) => void)[]
+
+const y1 = [S1.f, f];
+const y2 = [S1.f, g];
+const y3 = [S1.f, S2.f];
+const y4 = [S1.f, S2.g];
+
+// All assignments should be errors
+
+const x1: ((ctrl: number | string) => void)[] = y1;
+const x2: ((ctrl: number | string) => void)[] = y2;
+const x3: ((ctrl: number | string) => void)[] = y3;
+const x4: ((ctrl: number | string) => void)[] = y4;
\ No newline at end of file