From 6df05fa8cb07680a043275774c359e463e3955da Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 18 Apr 2019 12:53:08 -0700 Subject: [PATCH 1/7] Have signature identity checks look at the base constraint of type parameters, allow identical type parameter lists to merge in union signatures --- src/compiler/checker.ts | 68 +++++++++++++++---- src/compiler/types.ts | 4 +- .../genericSignatureIdentity.errors.txt | 33 +++++++++ ...naturesWithTypeParametersAndAny.errors.txt | 5 +- .../reference/promiseIdentity.errors.txt | 27 ++++++++ .../promiseIdentityWithAny.errors.txt | 16 +++++ .../baselines/reference/unionOfClassCalls.js | 21 ++++++ .../reference/unionOfClassCalls.symbols | 42 ++++++++++++ .../reference/unionOfClassCalls.types | 34 ++++++++++ tests/cases/compiler/unionOfClassCalls.ts | 14 ++++ 10 files changed, 250 insertions(+), 14 deletions(-) create mode 100644 tests/baselines/reference/genericSignatureIdentity.errors.txt create mode 100644 tests/baselines/reference/promiseIdentity.errors.txt create mode 100644 tests/baselines/reference/promiseIdentityWithAny.errors.txt create mode 100644 tests/baselines/reference/unionOfClassCalls.js create mode 100644 tests/baselines/reference/unionOfClassCalls.symbols create mode 100644 tests/baselines/reference/unionOfClassCalls.types create mode 100644 tests/cases/compiler/unionOfClassCalls.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 308b95c218611..ee6617f3a1d03 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6968,7 +6968,7 @@ namespace ts { if (signatures !== masterList) { const signature = signatures[0]; Debug.assert(!!signature, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass"); - results = signature.typeParameters && some(results, s => !!s.typeParameters) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, signature)); + results = signature.typeParameters && some(results, s => !!s.typeParameters && !compareTypeParametersIdentical(signature.typeParameters!, s.typeParameters)) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, signature)); if (!results) { break; } @@ -6979,18 +6979,34 @@ namespace ts { return result || emptyArray; } - function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined): Symbol | undefined { + function compareTypeParametersIdentical(sourceParams: readonly TypeParameter[], targetParams: readonly TypeParameter[]): boolean { + if (sourceParams.length !== targetParams.length) { + return false; + } + + for (let i = 0; i < sourceParams.length; i++) { + const source = sourceParams[i]; + const target = targetParams[i]; + if (source === target) continue; + if (!isTypeIdenticalTo(getConstraintFromTypeParameter(source) || unknownType, getConstraintFromTypeParameter(target) || unknownType)) return false; + if (!isTypeIdenticalTo(getDefaultFromTypeParameter(source) || unknownType, getDefaultFromTypeParameter(target) || unknownType)) return false; + } + + return true; + } + + function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined, mapper: TypeMapper | undefined): Symbol | undefined { if (!left || !right) { return left || right; } // A signature `this` type might be a read or a write position... It's very possible that it should be invariant // and we should refuse to merge signatures if there are `this` types and they do not match. However, so as to be // permissive when calling, for now, we'll union the `this` types just like the overlapping-union-signature check does - const thisType = getUnionType([getTypeOfSymbol(left), getTypeOfSymbol(right)], UnionReduction.Subtype); + const thisType = getUnionType([getTypeOfSymbol(left), instantiateType(getTypeOfSymbol(right), mapper)], UnionReduction.Subtype); return createSymbolWithType(left, thisType); } - function combineUnionParameters(left: Signature, right: Signature) { + function combineUnionParameters(left: Signature, right: Signature, mapper: TypeMapper | undefined) { const longest = getParameterCount(left) >= getParameterCount(right) ? left : right; const shorter = longest === left ? right : left; const longestCount = getParameterCount(longest); @@ -6998,8 +7014,14 @@ namespace ts { const needsExtraRestElement = eitherHasEffectiveRest && !hasEffectiveRestParameter(longest); const params = new Array(longestCount + (needsExtraRestElement ? 1 : 0)); for (let i = 0; i < longestCount; i++) { - const longestParamType = tryGetTypeAtPosition(longest, i)!; - const shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType; + let longestParamType = tryGetTypeAtPosition(longest, i)!; + if (longest === right) { + longestParamType = instantiateType(longestParamType, mapper); + } + let shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType; + if (shorter === right) { + shorterParamType = instantiateType(shorterParamType, mapper); + } const unionParamType = getIntersectionType([longestParamType, shorterParamType]); const isRestParam = eitherHasEffectiveRest && !needsExtraRestElement && i === (longestCount - 1); const isOptional = i >= getMinArgumentCount(longest) && i >= getMinArgumentCount(shorter); @@ -7015,21 +7037,29 @@ namespace ts { if (needsExtraRestElement) { const restParamSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "args" as __String); restParamSymbol.type = createArrayType(getTypeAtPosition(shorter, longestCount)); + if (shorter === right) { + restParamSymbol.type = instantiateType(restParamSymbol.type, mapper); + } params[longestCount] = restParamSymbol; } return params; } function combineSignaturesOfUnionMembers(left: Signature, right: Signature): Signature { + const typeParams = left.typeParameters || right.typeParameters; + let paramMapper: TypeMapper | undefined; + if (left.typeParameters && right.typeParameters) { + paramMapper = createTypeMapper(right.typeParameters, left.typeParameters); + } const declaration = left.declaration; - const params = combineUnionParameters(left, right); - const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter); + const params = combineUnionParameters(left, right, paramMapper); + const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter, paramMapper); const minArgCount = Math.max(left.minArgumentCount, right.minArgumentCount); const hasRestParam = left.hasRestParameter || right.hasRestParameter; const hasLiteralTypes = left.hasLiteralTypes || right.hasLiteralTypes; const result = createSignature( declaration, - left.typeParameters || right.typeParameters, + typeParams, thisParam, params, /*resolvedReturnType*/ undefined, @@ -7039,6 +7069,9 @@ namespace ts { hasLiteralTypes ); result.unionSignatures = concatenate(left.unionSignatures || [left], [right]); + if (paramMapper) { + result.mapper = left.mapper && left.unionSignatures ? combineTypeMappers(left.mapper, paramMapper) : paramMapper; + } return result; } @@ -8417,7 +8450,7 @@ namespace ts { return errorType; } let type = signature.target ? instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper!) : - signature.unionSignatures ? getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype) : + signature.unionSignatures ? instantiateType(getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype), signature.mapper) : getReturnTypeFromAnnotation(signature.declaration!) || isJSConstructor(signature.declaration) && getJSClassType(getSymbolOfNode(signature.declaration!)) || (nodeIsMissing((signature.declaration).body) ? anyType : getReturnTypeFromBody(signature.declaration)); @@ -8531,6 +8564,17 @@ namespace ts { return instantiateSignature(signature, createTypeEraser(signature.typeParameters!), /*eraseTypeParameters*/ true); } + function getConstrainedSignature(signature: Signature): Signature { + return signature.typeParameters ? + signature.constrainedSignatureCache || (signature.constrainedSignatureCache = createConstrainedSignature(signature)) : + signature; + } + + function createConstrainedSignature(signature: Signature) { + // Create an instantiation of the signature where all type arguments are their base constraint + return instantiateSignature(signature, t => contains(signature.typeParameters, t) ? getBaseConstraintOfType(t) || unknownType : t, /*eraseTypeParameters*/ true); + } + function getCanonicalSignature(signature: Signature): Signature { return signature.typeParameters ? signature.canonicalSignatureCache || (signature.canonicalSignatureCache = createCanonicalSignature(signature)) : @@ -13883,8 +13927,8 @@ namespace ts { } // Spec 1.0 Section 3.8.3 & 3.8.4: // M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N - source = getErasedSignature(source); - target = getErasedSignature(target); + source = getConstrainedSignature(source); + target = getConstrainedSignature(target); let result = Ternary.True; if (!ignoreThisTypes) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 826c567b06fbd..6e972b18f10cc 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4381,7 +4381,9 @@ namespace ts { /* @internal */ unionSignatures?: Signature[]; // Underlying signatures of a union signature /* @internal */ - erasedSignatureCache?: Signature; // Erased version of signature (deferred) + erasedSignatureCache?: Signature; // Erased version of signature (to any) (deferred) + /* @internal */ + constrainedSignatureCache?: Signature;// Erased version of signature (to constraints) (deferred) /* @internal */ canonicalSignatureCache?: Signature; // Canonical version of signature (deferred) /* @internal */ diff --git a/tests/baselines/reference/genericSignatureIdentity.errors.txt b/tests/baselines/reference/genericSignatureIdentity.errors.txt new file mode 100644 index 0000000000000..08bc0119ba4f7 --- /dev/null +++ b/tests/baselines/reference/genericSignatureIdentity.errors.txt @@ -0,0 +1,33 @@ +tests/cases/compiler/genericSignatureIdentity.ts(10,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '(x: T) => T', but here has type '(x: T) => T'. +tests/cases/compiler/genericSignatureIdentity.ts(14,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '(x: T) => T', but here has type '(x: T) => T'. +tests/cases/compiler/genericSignatureIdentity.ts(18,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '(x: T) => T', but here has type '(x: any) => any'. + + +==== tests/cases/compiler/genericSignatureIdentity.ts (3 errors) ==== + // This test is here to remind us of our current limits of type identity checking. + // Ideally all of the below declarations would be considered different (and thus errors) + // but they aren't because we erase type parameters to type any and don't check that + // constraints are identical. + + var x: { + (x: T): T; + }; + + var x: { + ~ +!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '(x: T) => T', but here has type '(x: T) => T'. + (x: T): T; + }; + + var x: { + ~ +!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '(x: T) => T', but here has type '(x: T) => T'. + (x: T): T; + }; + + var x: { + ~ +!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '(x: T) => T', but here has type '(x: any) => any'. + (x: any): any; + }; + \ No newline at end of file diff --git a/tests/baselines/reference/identityForSignaturesWithTypeParametersAndAny.errors.txt b/tests/baselines/reference/identityForSignaturesWithTypeParametersAndAny.errors.txt index 6023928022699..40a9651acec7d 100644 --- a/tests/baselines/reference/identityForSignaturesWithTypeParametersAndAny.errors.txt +++ b/tests/baselines/reference/identityForSignaturesWithTypeParametersAndAny.errors.txt @@ -1,12 +1,15 @@ +tests/cases/compiler/identityForSignaturesWithTypeParametersAndAny.ts(2,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'f' must be of type '(x: T, y: U) => T', but here has type '(x: any, y: any) => any'. tests/cases/compiler/identityForSignaturesWithTypeParametersAndAny.ts(5,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'g' must be of type '(x: T, y: U) => T', but here has type '(x: any, y: any) => any'. tests/cases/compiler/identityForSignaturesWithTypeParametersAndAny.ts(8,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'h' must be of type '(x: T, y: U) => T', but here has type '(x: any, y: any) => any'. tests/cases/compiler/identityForSignaturesWithTypeParametersAndAny.ts(11,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'i' must be of type '(x: T, y: U) => T', but here has type '(x: any, y: string) => any'. tests/cases/compiler/identityForSignaturesWithTypeParametersAndAny.ts(14,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'j' must be of type '(x: T, y: U) => T', but here has type '(x: any, y: any) => string'. -==== tests/cases/compiler/identityForSignaturesWithTypeParametersAndAny.ts (4 errors) ==== +==== tests/cases/compiler/identityForSignaturesWithTypeParametersAndAny.ts (5 errors) ==== var f: (x: T, y: U) => T; var f: (x: any, y: any) => any; + ~ +!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'f' must be of type '(x: T, y: U) => T', but here has type '(x: any, y: any) => any'. var g: (x: T, y: U) => T; var g: (x: any, y: any) => any; diff --git a/tests/baselines/reference/promiseIdentity.errors.txt b/tests/baselines/reference/promiseIdentity.errors.txt new file mode 100644 index 0000000000000..96bdc0d5196ab --- /dev/null +++ b/tests/baselines/reference/promiseIdentity.errors.txt @@ -0,0 +1,27 @@ +tests/cases/compiler/promiseIdentity.ts(21,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'y' must be of type 'IPromise2', but here has type 'Promise2'. + + +==== tests/cases/compiler/promiseIdentity.ts (1 errors) ==== + export interface IPromise { + then(callback: (x: T) => IPromise): IPromise; + } + interface Promise { + then(callback: (x: T) => Promise): Promise; + } + var x: IPromise; + var x: Promise; + + + interface IPromise2 { + then(callback: (x: T) => IPromise2): IPromise2; + } + interface Promise2 { + then(callback: (x: V) => Promise2): Promise2; // Uses V instead of T in callback's parameter + } + + // Ok because T in this particular Promise2 is any, as are all the U and W references. + // Also, the V of Promise2 happens to coincide with the T of IPromise2 (they are both string). + var y: IPromise2; + var y: Promise2; + ~ +!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'y' must be of type 'IPromise2', but here has type 'Promise2'. \ No newline at end of file diff --git a/tests/baselines/reference/promiseIdentityWithAny.errors.txt b/tests/baselines/reference/promiseIdentityWithAny.errors.txt new file mode 100644 index 0000000000000..ebbcc3febf4dc --- /dev/null +++ b/tests/baselines/reference/promiseIdentityWithAny.errors.txt @@ -0,0 +1,16 @@ +tests/cases/compiler/promiseIdentityWithAny.ts(10,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type 'IPromise', but here has type 'Promise'. + + +==== tests/cases/compiler/promiseIdentityWithAny.ts (1 errors) ==== + export interface IPromise { + then(callback: (x: T) => IPromise): IPromise; + } + export interface Promise { + then(callback: (x: T) => Promise): Promise; + } + + // Should be ok because signature type parameters get erased to any + var x: IPromise; + var x: Promise; + ~ +!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type 'IPromise', but here has type 'Promise'. \ No newline at end of file diff --git a/tests/baselines/reference/unionOfClassCalls.js b/tests/baselines/reference/unionOfClassCalls.js new file mode 100644 index 0000000000000..6e23f70d6c45d --- /dev/null +++ b/tests/baselines/reference/unionOfClassCalls.js @@ -0,0 +1,21 @@ +//// [unionOfClassCalls.ts] +declare class Test { + obj: T; + get(k: K): T[K]; +} + +interface A { t: "A" } +interface B { t: "B" } + +declare const tmp: Test | Test; + +switch (tmp.get('t')) { + case 'A': break; + case 'B': break; +} + +//// [unionOfClassCalls.js] +switch (tmp.get('t')) { + case 'A': break; + case 'B': break; +} diff --git a/tests/baselines/reference/unionOfClassCalls.symbols b/tests/baselines/reference/unionOfClassCalls.symbols new file mode 100644 index 0000000000000..8be25548d8ee2 --- /dev/null +++ b/tests/baselines/reference/unionOfClassCalls.symbols @@ -0,0 +1,42 @@ +=== tests/cases/compiler/unionOfClassCalls.ts === +declare class Test { +>Test : Symbol(Test, Decl(unionOfClassCalls.ts, 0, 0)) +>T : Symbol(T, Decl(unionOfClassCalls.ts, 0, 19)) + + obj: T; +>obj : Symbol(Test.obj, Decl(unionOfClassCalls.ts, 0, 23)) +>T : Symbol(T, Decl(unionOfClassCalls.ts, 0, 19)) + + get(k: K): T[K]; +>get : Symbol(Test.get, Decl(unionOfClassCalls.ts, 1, 11)) +>K : Symbol(K, Decl(unionOfClassCalls.ts, 2, 8)) +>T : Symbol(T, Decl(unionOfClassCalls.ts, 0, 19)) +>k : Symbol(k, Decl(unionOfClassCalls.ts, 2, 27)) +>K : Symbol(K, Decl(unionOfClassCalls.ts, 2, 8)) +>T : Symbol(T, Decl(unionOfClassCalls.ts, 0, 19)) +>K : Symbol(K, Decl(unionOfClassCalls.ts, 2, 8)) +} + +interface A { t: "A" } +>A : Symbol(A, Decl(unionOfClassCalls.ts, 3, 1)) +>t : Symbol(A.t, Decl(unionOfClassCalls.ts, 5, 13)) + +interface B { t: "B" } +>B : Symbol(B, Decl(unionOfClassCalls.ts, 5, 22)) +>t : Symbol(B.t, Decl(unionOfClassCalls.ts, 6, 13)) + +declare const tmp: Test | Test; +>tmp : Symbol(tmp, Decl(unionOfClassCalls.ts, 8, 13)) +>Test : Symbol(Test, Decl(unionOfClassCalls.ts, 0, 0)) +>A : Symbol(A, Decl(unionOfClassCalls.ts, 3, 1)) +>Test : Symbol(Test, Decl(unionOfClassCalls.ts, 0, 0)) +>B : Symbol(B, Decl(unionOfClassCalls.ts, 5, 22)) + +switch (tmp.get('t')) { +>tmp.get : Symbol(Test.get, Decl(unionOfClassCalls.ts, 1, 11), Decl(unionOfClassCalls.ts, 1, 11)) +>tmp : Symbol(tmp, Decl(unionOfClassCalls.ts, 8, 13)) +>get : Symbol(Test.get, Decl(unionOfClassCalls.ts, 1, 11), Decl(unionOfClassCalls.ts, 1, 11)) + + case 'A': break; + case 'B': break; +} diff --git a/tests/baselines/reference/unionOfClassCalls.types b/tests/baselines/reference/unionOfClassCalls.types new file mode 100644 index 0000000000000..daea1f6f733b5 --- /dev/null +++ b/tests/baselines/reference/unionOfClassCalls.types @@ -0,0 +1,34 @@ +=== tests/cases/compiler/unionOfClassCalls.ts === +declare class Test { +>Test : Test + + obj: T; +>obj : T + + get(k: K): T[K]; +>get : (k: K) => T[K] +>k : K +} + +interface A { t: "A" } +>t : "A" + +interface B { t: "B" } +>t : "B" + +declare const tmp: Test | Test; +>tmp : Test | Test + +switch (tmp.get('t')) { +>tmp.get('t') : "A" | "B" +>tmp.get : ((k: K) => A[K]) | ((k: K) => B[K]) +>tmp : Test | Test +>get : ((k: K) => A[K]) | ((k: K) => B[K]) +>'t' : "t" + + case 'A': break; +>'A' : "A" + + case 'B': break; +>'B' : "B" +} diff --git a/tests/cases/compiler/unionOfClassCalls.ts b/tests/cases/compiler/unionOfClassCalls.ts new file mode 100644 index 0000000000000..28ea00fac1821 --- /dev/null +++ b/tests/cases/compiler/unionOfClassCalls.ts @@ -0,0 +1,14 @@ +declare class Test { + obj: T; + get(k: K): T[K]; +} + +interface A { t: "A" } +interface B { t: "B" } + +declare const tmp: Test | Test; + +switch (tmp.get('t')) { + case 'A': break; + case 'B': break; +} \ No newline at end of file From d36dfcb278caedc708bc6cc13973f1bbaf90d0bd Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 18 Apr 2019 14:33:43 -0700 Subject: [PATCH 2/7] Update text in fourslash test --- tests/cases/fourslash/completionEntryForUnionMethod.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cases/fourslash/completionEntryForUnionMethod.ts b/tests/cases/fourslash/completionEntryForUnionMethod.ts index 80fa6d5dd6e6c..b309d453f16e2 100644 --- a/tests/cases/fourslash/completionEntryForUnionMethod.ts +++ b/tests/cases/fourslash/completionEntryForUnionMethod.ts @@ -3,7 +3,7 @@ ////var y: Array|Array; ////y.map/**/( -const text = "(property) Array.map: ((callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | ((callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[])"; +const text = "(method) Array.map(callbackfn: ((value: string, index: number, array: string[]) => unknown) & ((value: number, index: number, array: number[]) => unknown), thisArg: any): unknown[]"; const documentation = "Calls a defined callback function on each element of an array, and returns an array that contains the results."; verify.quickInfoAt("", text, documentation); From 0580ccaf8591fac3ee91602ba8aef7fd8f6065db Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 18 Apr 2019 14:38:38 -0700 Subject: [PATCH 3/7] Add whitespace to fix lint, remove duplicate impl --- src/compiler/checker.ts | 7 +------ src/compiler/types.ts | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ee6617f3a1d03..bb16748779d39 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8566,15 +8566,10 @@ namespace ts { function getConstrainedSignature(signature: Signature): Signature { return signature.typeParameters ? - signature.constrainedSignatureCache || (signature.constrainedSignatureCache = createConstrainedSignature(signature)) : + signature.constrainedSignatureCache || (signature.constrainedSignatureCache = getBaseSignature(signature)) : signature; } - function createConstrainedSignature(signature: Signature) { - // Create an instantiation of the signature where all type arguments are their base constraint - return instantiateSignature(signature, t => contains(signature.typeParameters, t) ? getBaseConstraintOfType(t) || unknownType : t, /*eraseTypeParameters*/ true); - } - function getCanonicalSignature(signature: Signature): Signature { return signature.typeParameters ? signature.canonicalSignatureCache || (signature.canonicalSignatureCache = createCanonicalSignature(signature)) : diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6e972b18f10cc..6fa5ba96c1007 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4383,7 +4383,7 @@ namespace ts { /* @internal */ erasedSignatureCache?: Signature; // Erased version of signature (to any) (deferred) /* @internal */ - constrainedSignatureCache?: Signature;// Erased version of signature (to constraints) (deferred) + constrainedSignatureCache?: Signature; // Erased version of signature (to constraints) (deferred) /* @internal */ canonicalSignatureCache?: Signature; // Canonical version of signature (deferred) /* @internal */ From 01dea4beaff142515d42a7b33ccda9918baf5d57 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 18 Apr 2019 14:45:39 -0700 Subject: [PATCH 4/7] Consolidate names --- src/compiler/checker.ts | 10 +++++----- src/compiler/types.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bb16748779d39..c08b4e275a076 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8564,9 +8564,9 @@ namespace ts { return instantiateSignature(signature, createTypeEraser(signature.typeParameters!), /*eraseTypeParameters*/ true); } - function getConstrainedSignature(signature: Signature): Signature { + function getBaseSignature(signature: Signature): Signature { return signature.typeParameters ? - signature.constrainedSignatureCache || (signature.constrainedSignatureCache = getBaseSignature(signature)) : + signature.baseSignatureCache || (signature.baseSignatureCache = createBaseSignature(signature)) : signature; } @@ -8589,7 +8589,7 @@ namespace ts { isInJSFile(signature.declaration)); } - function getBaseSignature(signature: Signature) { + function createBaseSignature(signature: Signature) { const typeParameters = signature.typeParameters; if (typeParameters) { const typeEraser = createTypeEraser(typeParameters); @@ -13922,8 +13922,8 @@ namespace ts { } // Spec 1.0 Section 3.8.3 & 3.8.4: // M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N - source = getConstrainedSignature(source); - target = getConstrainedSignature(target); + source = getBaseSignature(source); + target = getBaseSignature(target); let result = Ternary.True; if (!ignoreThisTypes) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6fa5ba96c1007..1f62a96cf8cbe 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4383,7 +4383,7 @@ namespace ts { /* @internal */ erasedSignatureCache?: Signature; // Erased version of signature (to any) (deferred) /* @internal */ - constrainedSignatureCache?: Signature; // Erased version of signature (to constraints) (deferred) + baseSignatureCache?: Signature; // Erased version of signature (to constraints) (deferred) /* @internal */ canonicalSignatureCache?: Signature; // Canonical version of signature (deferred) /* @internal */ From e14c58e0874d4226aeaf28e73bc159db54752b0e Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 4 Mar 2020 04:59:11 -0800 Subject: [PATCH 5/7] Remove comparisons of type parameter defaults, add more test cases --- src/compiler/checker.ts | 10 +- .../reference/unionOfClassCalls.errors.txt | 85 ++++++++ .../baselines/reference/unionOfClassCalls.js | 102 ++++++++- .../reference/unionOfClassCalls.symbols | 205 ++++++++++++++++-- .../reference/unionOfClassCalls.types | 191 ++++++++++++++++ tests/cases/compiler/unionOfClassCalls.ts | 65 +++++- 6 files changed, 634 insertions(+), 24 deletions(-) create mode 100644 tests/baselines/reference/unionOfClassCalls.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7bddbbd8d7cef..6855bd974df52 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9301,12 +9301,17 @@ namespace ts { return false; } + const mapper = createTypeMapper(targetParams, sourceParams); for (let i = 0; i < sourceParams.length; i++) { const source = sourceParams[i]; const target = targetParams[i]; if (source === target) continue; - if (!isTypeIdenticalTo(getConstraintFromTypeParameter(source) || unknownType, getConstraintFromTypeParameter(target) || unknownType)) return false; - if (!isTypeIdenticalTo(getDefaultFromTypeParameter(source) || unknownType, getDefaultFromTypeParameter(target) || unknownType)) return false; + // We instantiate the target type parameter constraints into the source types so we can recognize `` as the same as `` + if (!isTypeIdenticalTo(getConstraintFromTypeParameter(source) || unknownType, instantiateType(getConstraintFromTypeParameter(target) || unknownType, mapper))) return false; + // We don't compare defaults - we just use the type parameter defaults from the first signature that seems to match. + // It might make sense to combine these defaults in the future, but doing so intelligently requires knowing + // if the parameter is used covariantly or contravariantly (so we intersect if it's used like a parameter or union if used like a return type) + // and, since it's just an inference _default_, just picking one arbitrarily works OK. } return true; @@ -9374,6 +9379,7 @@ namespace ts { let paramMapper: TypeMapper | undefined; if (left.typeParameters && right.typeParameters) { paramMapper = createTypeMapper(right.typeParameters, left.typeParameters); + // We just use the type parameter defaults from the first signature } const declaration = left.declaration; const params = combineUnionParameters(left, right, paramMapper); diff --git a/tests/baselines/reference/unionOfClassCalls.errors.txt b/tests/baselines/reference/unionOfClassCalls.errors.txt new file mode 100644 index 0000000000000..386d34414bbf0 --- /dev/null +++ b/tests/baselines/reference/unionOfClassCalls.errors.txt @@ -0,0 +1,85 @@ +tests/cases/compiler/unionOfClassCalls.ts(28,5): error TS2349: This expression is not callable. + Each member of the union type '{ (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; (callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; } | { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; }' has signatures, but none of those signatures are compatible with each other. + + +==== tests/cases/compiler/unionOfClassCalls.ts (1 errors) ==== + // from https://github.com/microsoft/TypeScript/issues/30717 + declare class Test { + obj: T; + get(k: K): T[K]; + } + + interface A { t: "A" } + interface B { t: "B" } + + declare const tmp: Test | Test; + + switch (tmp.get('t')) { + case 'A': break; + case 'B': break; + } + + // from https://github.com/microsoft/TypeScript/issues/36390 + + const arr: number[] | string[] = []; // Works with Array + const arr1: number[] = []; + const arr2: string[] = []; + + arr.map((a: number | string, index: number) => { + return index + }) + + // This case still doesn't work because `reduce` has multiple overloads :( + arr.reduce((acc: Array, a: number | string, index: number) => { + ~~~~~~ +!!! error TS2349: This expression is not callable. +!!! error TS2349: Each member of the union type '{ (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; (callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; } | { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; }' has signatures, but none of those signatures are compatible with each other. + return [] + }, []) + + arr.forEach((a: number | string, index: number) => { + return index + }) + + arr1.map((a: number, index: number) => { + return index + }) + + arr1.reduce((acc: number[], a: number, index: number) => { + return [a] + }, []) + + arr1.forEach((a: number, index: number) => { + return index + }) + arr2.map((a: string, index: number) => { + return index + }) + + arr2.reduce((acc: string[], a: string, index: number) => { + return [] + }, []) + + arr2.forEach((a: string, index: number) => { + return index + }) + + // from https://github.com/microsoft/TypeScript/issues/36307 + + declare class Foo { + doThing(): Promise + } + + declare class Bar extends Foo { + bar: number; + } + declare class Baz extends Foo { + baz: number; + } + + declare var a: Bar | Baz; + // note, you must annotate `result` for now + a.doThing().then((result: Bar | Baz) => { + // whatever + }); + \ No newline at end of file diff --git a/tests/baselines/reference/unionOfClassCalls.js b/tests/baselines/reference/unionOfClassCalls.js index 6e23f70d6c45d..706af1b83990b 100644 --- a/tests/baselines/reference/unionOfClassCalls.js +++ b/tests/baselines/reference/unionOfClassCalls.js @@ -1,4 +1,5 @@ //// [unionOfClassCalls.ts] +// from https://github.com/microsoft/TypeScript/issues/30717 declare class Test { obj: T; get(k: K): T[K]; @@ -12,10 +13,109 @@ declare const tmp: Test | Test; switch (tmp.get('t')) { case 'A': break; case 'B': break; -} +} + +// from https://github.com/microsoft/TypeScript/issues/36390 + +const arr: number[] | string[] = []; // Works with Array +const arr1: number[] = []; +const arr2: string[] = []; + +arr.map((a: number | string, index: number) => { + return index +}) + +// This case still doesn't work because `reduce` has multiple overloads :( +arr.reduce((acc: Array, a: number | string, index: number) => { + return [] +}, []) + +arr.forEach((a: number | string, index: number) => { + return index +}) + +arr1.map((a: number, index: number) => { + return index +}) + +arr1.reduce((acc: number[], a: number, index: number) => { + return [a] +}, []) + +arr1.forEach((a: number, index: number) => { + return index +}) +arr2.map((a: string, index: number) => { + return index +}) + +arr2.reduce((acc: string[], a: string, index: number) => { + return [] +}, []) + +arr2.forEach((a: string, index: number) => { + return index +}) + +// from https://github.com/microsoft/TypeScript/issues/36307 + +declare class Foo { + doThing(): Promise +} + +declare class Bar extends Foo { + bar: number; +} +declare class Baz extends Foo { + baz: number; +} + +declare var a: Bar | Baz; +// note, you must annotate `result` for now +a.doThing().then((result: Bar | Baz) => { + // whatever +}); + //// [unionOfClassCalls.js] +"use strict"; switch (tmp.get('t')) { case 'A': break; case 'B': break; } +// from https://github.com/microsoft/TypeScript/issues/36390 +var arr = []; // Works with Array +var arr1 = []; +var arr2 = []; +arr.map(function (a, index) { + return index; +}); +// This case still doesn't work because `reduce` has multiple overloads :( +arr.reduce(function (acc, a, index) { + return []; +}, []); +arr.forEach(function (a, index) { + return index; +}); +arr1.map(function (a, index) { + return index; +}); +arr1.reduce(function (acc, a, index) { + return [a]; +}, []); +arr1.forEach(function (a, index) { + return index; +}); +arr2.map(function (a, index) { + return index; +}); +arr2.reduce(function (acc, a, index) { + return []; +}, []); +arr2.forEach(function (a, index) { + return index; +}); +// note, you must annotate `result` for now +a.doThing().then(function (result) { + // whatever +}); diff --git a/tests/baselines/reference/unionOfClassCalls.symbols b/tests/baselines/reference/unionOfClassCalls.symbols index 8be25548d8ee2..03155a66c5a10 100644 --- a/tests/baselines/reference/unionOfClassCalls.symbols +++ b/tests/baselines/reference/unionOfClassCalls.symbols @@ -1,42 +1,207 @@ === tests/cases/compiler/unionOfClassCalls.ts === +// from https://github.com/microsoft/TypeScript/issues/30717 declare class Test { >Test : Symbol(Test, Decl(unionOfClassCalls.ts, 0, 0)) ->T : Symbol(T, Decl(unionOfClassCalls.ts, 0, 19)) +>T : Symbol(T, Decl(unionOfClassCalls.ts, 1, 19)) obj: T; ->obj : Symbol(Test.obj, Decl(unionOfClassCalls.ts, 0, 23)) ->T : Symbol(T, Decl(unionOfClassCalls.ts, 0, 19)) +>obj : Symbol(Test.obj, Decl(unionOfClassCalls.ts, 1, 23)) +>T : Symbol(T, Decl(unionOfClassCalls.ts, 1, 19)) get(k: K): T[K]; ->get : Symbol(Test.get, Decl(unionOfClassCalls.ts, 1, 11)) ->K : Symbol(K, Decl(unionOfClassCalls.ts, 2, 8)) ->T : Symbol(T, Decl(unionOfClassCalls.ts, 0, 19)) ->k : Symbol(k, Decl(unionOfClassCalls.ts, 2, 27)) ->K : Symbol(K, Decl(unionOfClassCalls.ts, 2, 8)) ->T : Symbol(T, Decl(unionOfClassCalls.ts, 0, 19)) ->K : Symbol(K, Decl(unionOfClassCalls.ts, 2, 8)) +>get : Symbol(Test.get, Decl(unionOfClassCalls.ts, 2, 11)) +>K : Symbol(K, Decl(unionOfClassCalls.ts, 3, 8)) +>T : Symbol(T, Decl(unionOfClassCalls.ts, 1, 19)) +>k : Symbol(k, Decl(unionOfClassCalls.ts, 3, 27)) +>K : Symbol(K, Decl(unionOfClassCalls.ts, 3, 8)) +>T : Symbol(T, Decl(unionOfClassCalls.ts, 1, 19)) +>K : Symbol(K, Decl(unionOfClassCalls.ts, 3, 8)) } interface A { t: "A" } ->A : Symbol(A, Decl(unionOfClassCalls.ts, 3, 1)) ->t : Symbol(A.t, Decl(unionOfClassCalls.ts, 5, 13)) +>A : Symbol(A, Decl(unionOfClassCalls.ts, 4, 1)) +>t : Symbol(A.t, Decl(unionOfClassCalls.ts, 6, 13)) interface B { t: "B" } ->B : Symbol(B, Decl(unionOfClassCalls.ts, 5, 22)) ->t : Symbol(B.t, Decl(unionOfClassCalls.ts, 6, 13)) +>B : Symbol(B, Decl(unionOfClassCalls.ts, 6, 22)) +>t : Symbol(B.t, Decl(unionOfClassCalls.ts, 7, 13)) declare const tmp: Test | Test; ->tmp : Symbol(tmp, Decl(unionOfClassCalls.ts, 8, 13)) +>tmp : Symbol(tmp, Decl(unionOfClassCalls.ts, 9, 13)) >Test : Symbol(Test, Decl(unionOfClassCalls.ts, 0, 0)) ->A : Symbol(A, Decl(unionOfClassCalls.ts, 3, 1)) +>A : Symbol(A, Decl(unionOfClassCalls.ts, 4, 1)) >Test : Symbol(Test, Decl(unionOfClassCalls.ts, 0, 0)) ->B : Symbol(B, Decl(unionOfClassCalls.ts, 5, 22)) +>B : Symbol(B, Decl(unionOfClassCalls.ts, 6, 22)) switch (tmp.get('t')) { ->tmp.get : Symbol(Test.get, Decl(unionOfClassCalls.ts, 1, 11), Decl(unionOfClassCalls.ts, 1, 11)) ->tmp : Symbol(tmp, Decl(unionOfClassCalls.ts, 8, 13)) ->get : Symbol(Test.get, Decl(unionOfClassCalls.ts, 1, 11), Decl(unionOfClassCalls.ts, 1, 11)) +>tmp.get : Symbol(Test.get, Decl(unionOfClassCalls.ts, 2, 11), Decl(unionOfClassCalls.ts, 2, 11)) +>tmp : Symbol(tmp, Decl(unionOfClassCalls.ts, 9, 13)) +>get : Symbol(Test.get, Decl(unionOfClassCalls.ts, 2, 11), Decl(unionOfClassCalls.ts, 2, 11)) case 'A': break; case 'B': break; } + +// from https://github.com/microsoft/TypeScript/issues/36390 + +const arr: number[] | string[] = []; // Works with Array +>arr : Symbol(arr, Decl(unionOfClassCalls.ts, 18, 5)) + +const arr1: number[] = []; +>arr1 : Symbol(arr1, Decl(unionOfClassCalls.ts, 19, 5)) + +const arr2: string[] = []; +>arr2 : Symbol(arr2, Decl(unionOfClassCalls.ts, 20, 5)) + +arr.map((a: number | string, index: number) => { +>arr.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>arr : Symbol(arr, Decl(unionOfClassCalls.ts, 18, 5)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(unionOfClassCalls.ts, 22, 9)) +>index : Symbol(index, Decl(unionOfClassCalls.ts, 22, 28)) + + return index +>index : Symbol(index, Decl(unionOfClassCalls.ts, 22, 28)) + +}) + +// This case still doesn't work because `reduce` has multiple overloads :( +arr.reduce((acc: Array, a: number | string, index: number) => { +>arr.reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --) ... and 1 more) +>arr : Symbol(arr, Decl(unionOfClassCalls.ts, 18, 5)) +>reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --) ... and 1 more) +>acc : Symbol(acc, Decl(unionOfClassCalls.ts, 27, 12)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(unionOfClassCalls.ts, 27, 31)) +>index : Symbol(index, Decl(unionOfClassCalls.ts, 27, 51)) + + return [] +}, []) + +arr.forEach((a: number | string, index: number) => { +>arr.forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>arr : Symbol(arr, Decl(unionOfClassCalls.ts, 18, 5)) +>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(unionOfClassCalls.ts, 31, 13)) +>index : Symbol(index, Decl(unionOfClassCalls.ts, 31, 32)) + + return index +>index : Symbol(index, Decl(unionOfClassCalls.ts, 31, 32)) + +}) + +arr1.map((a: number, index: number) => { +>arr1.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>arr1 : Symbol(arr1, Decl(unionOfClassCalls.ts, 19, 5)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(unionOfClassCalls.ts, 35, 10)) +>index : Symbol(index, Decl(unionOfClassCalls.ts, 35, 20)) + + return index +>index : Symbol(index, Decl(unionOfClassCalls.ts, 35, 20)) + +}) + +arr1.reduce((acc: number[], a: number, index: number) => { +>arr1.reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>arr1 : Symbol(arr1, Decl(unionOfClassCalls.ts, 19, 5)) +>reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>acc : Symbol(acc, Decl(unionOfClassCalls.ts, 39, 13)) +>a : Symbol(a, Decl(unionOfClassCalls.ts, 39, 27)) +>index : Symbol(index, Decl(unionOfClassCalls.ts, 39, 38)) + + return [a] +>a : Symbol(a, Decl(unionOfClassCalls.ts, 39, 27)) + +}, []) + +arr1.forEach((a: number, index: number) => { +>arr1.forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>arr1 : Symbol(arr1, Decl(unionOfClassCalls.ts, 19, 5)) +>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(unionOfClassCalls.ts, 43, 14)) +>index : Symbol(index, Decl(unionOfClassCalls.ts, 43, 24)) + + return index +>index : Symbol(index, Decl(unionOfClassCalls.ts, 43, 24)) + +}) +arr2.map((a: string, index: number) => { +>arr2.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>arr2 : Symbol(arr2, Decl(unionOfClassCalls.ts, 20, 5)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(unionOfClassCalls.ts, 46, 10)) +>index : Symbol(index, Decl(unionOfClassCalls.ts, 46, 21)) + + return index +>index : Symbol(index, Decl(unionOfClassCalls.ts, 46, 21)) + +}) + +arr2.reduce((acc: string[], a: string, index: number) => { +>arr2.reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>arr2 : Symbol(arr2, Decl(unionOfClassCalls.ts, 20, 5)) +>reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>acc : Symbol(acc, Decl(unionOfClassCalls.ts, 50, 13)) +>a : Symbol(a, Decl(unionOfClassCalls.ts, 50, 27)) +>index : Symbol(index, Decl(unionOfClassCalls.ts, 50, 38)) + + return [] +}, []) + +arr2.forEach((a: string, index: number) => { +>arr2.forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>arr2 : Symbol(arr2, Decl(unionOfClassCalls.ts, 20, 5)) +>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(unionOfClassCalls.ts, 54, 14)) +>index : Symbol(index, Decl(unionOfClassCalls.ts, 54, 24)) + + return index +>index : Symbol(index, Decl(unionOfClassCalls.ts, 54, 24)) + +}) + +// from https://github.com/microsoft/TypeScript/issues/36307 + +declare class Foo { +>Foo : Symbol(Foo, Decl(unionOfClassCalls.ts, 56, 2)) + + doThing(): Promise +>doThing : Symbol(Foo.doThing, Decl(unionOfClassCalls.ts, 60, 19)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +} + +declare class Bar extends Foo { +>Bar : Symbol(Bar, Decl(unionOfClassCalls.ts, 62, 1)) +>Foo : Symbol(Foo, Decl(unionOfClassCalls.ts, 56, 2)) + + bar: number; +>bar : Symbol(Bar.bar, Decl(unionOfClassCalls.ts, 64, 31)) +} +declare class Baz extends Foo { +>Baz : Symbol(Baz, Decl(unionOfClassCalls.ts, 66, 1)) +>Foo : Symbol(Foo, Decl(unionOfClassCalls.ts, 56, 2)) + + baz: number; +>baz : Symbol(Baz.baz, Decl(unionOfClassCalls.ts, 67, 31)) +} + +declare var a: Bar | Baz; +>a : Symbol(a, Decl(unionOfClassCalls.ts, 71, 11)) +>Bar : Symbol(Bar, Decl(unionOfClassCalls.ts, 62, 1)) +>Baz : Symbol(Baz, Decl(unionOfClassCalls.ts, 66, 1)) + +// note, you must annotate `result` for now +a.doThing().then((result: Bar | Baz) => { +>a.doThing().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>a.doThing : Symbol(Foo.doThing, Decl(unionOfClassCalls.ts, 60, 19), Decl(unionOfClassCalls.ts, 60, 19)) +>a : Symbol(a, Decl(unionOfClassCalls.ts, 71, 11)) +>doThing : Symbol(Foo.doThing, Decl(unionOfClassCalls.ts, 60, 19), Decl(unionOfClassCalls.ts, 60, 19)) +>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>result : Symbol(result, Decl(unionOfClassCalls.ts, 73, 18)) +>Bar : Symbol(Bar, Decl(unionOfClassCalls.ts, 62, 1)) +>Baz : Symbol(Baz, Decl(unionOfClassCalls.ts, 66, 1)) + + // whatever +}); + diff --git a/tests/baselines/reference/unionOfClassCalls.types b/tests/baselines/reference/unionOfClassCalls.types index daea1f6f733b5..bcae9d15d6801 100644 --- a/tests/baselines/reference/unionOfClassCalls.types +++ b/tests/baselines/reference/unionOfClassCalls.types @@ -1,4 +1,5 @@ === tests/cases/compiler/unionOfClassCalls.ts === +// from https://github.com/microsoft/TypeScript/issues/30717 declare class Test { >Test : Test @@ -32,3 +33,193 @@ switch (tmp.get('t')) { case 'B': break; >'B' : "B" } + +// from https://github.com/microsoft/TypeScript/issues/36390 + +const arr: number[] | string[] = []; // Works with Array +>arr : number[] | string[] +>[] : never[] + +const arr1: number[] = []; +>arr1 : number[] +>[] : never[] + +const arr2: string[] = []; +>arr2 : string[] +>[] : never[] + +arr.map((a: number | string, index: number) => { +>arr.map((a: number | string, index: number) => { return index}) : number[] +>arr.map : ((callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]) | ((callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) +>arr : number[] | string[] +>map : ((callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]) | ((callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) +>(a: number | string, index: number) => { return index} : (a: string | number, index: number) => number +>a : string | number +>index : number + + return index +>index : number + +}) + +// This case still doesn't work because `reduce` has multiple overloads :( +arr.reduce((acc: Array, a: number | string, index: number) => { +>arr.reduce((acc: Array, a: number | string, index: number) => { return []}, []) : any +>arr.reduce : { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; (callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; } | { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; } +>arr : number[] | string[] +>reduce : { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; (callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; } | { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; } +>(acc: Array, a: number | string, index: number) => { return []} : (acc: string[], a: string | number, index: number) => never[] +>acc : string[] +>a : string | number +>index : number + + return [] +>[] : never[] + +}, []) +>[] : never[] + +arr.forEach((a: number | string, index: number) => { +>arr.forEach((a: number | string, index: number) => { return index}) : void +>arr.forEach : ((callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void) | ((callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void) +>arr : number[] | string[] +>forEach : ((callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void) | ((callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void) +>(a: number | string, index: number) => { return index} : (a: string | number, index: number) => number +>a : string | number +>index : number + + return index +>index : number + +}) + +arr1.map((a: number, index: number) => { +>arr1.map((a: number, index: number) => { return index}) : number[] +>arr1.map : (callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[] +>arr1 : number[] +>map : (callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[] +>(a: number, index: number) => { return index} : (a: number, index: number) => number +>a : number +>index : number + + return index +>index : number + +}) + +arr1.reduce((acc: number[], a: number, index: number) => { +>arr1.reduce((acc: number[], a: number, index: number) => { return [a]}, []) : number[] +>arr1.reduce : { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; (callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; } +>arr1 : number[] +>reduce : { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; (callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; } +>(acc: number[], a: number, index: number) => { return [a]} : (acc: number[], a: number, index: number) => number[] +>acc : number[] +>a : number +>index : number + + return [a] +>[a] : number[] +>a : number + +}, []) +>[] : never[] + +arr1.forEach((a: number, index: number) => { +>arr1.forEach((a: number, index: number) => { return index}) : void +>arr1.forEach : (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void +>arr1 : number[] +>forEach : (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void +>(a: number, index: number) => { return index} : (a: number, index: number) => number +>a : number +>index : number + + return index +>index : number + +}) +arr2.map((a: string, index: number) => { +>arr2.map((a: string, index: number) => { return index}) : number[] +>arr2.map : (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[] +>arr2 : string[] +>map : (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[] +>(a: string, index: number) => { return index} : (a: string, index: number) => number +>a : string +>index : number + + return index +>index : number + +}) + +arr2.reduce((acc: string[], a: string, index: number) => { +>arr2.reduce((acc: string[], a: string, index: number) => { return []}, []) : never[] +>arr2.reduce : { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; } +>arr2 : string[] +>reduce : { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; } +>(acc: string[], a: string, index: number) => { return []} : (acc: string[], a: string, index: number) => never[] +>acc : string[] +>a : string +>index : number + + return [] +>[] : never[] + +}, []) +>[] : never[] + +arr2.forEach((a: string, index: number) => { +>arr2.forEach((a: string, index: number) => { return index}) : void +>arr2.forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void +>arr2 : string[] +>forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void +>(a: string, index: number) => { return index} : (a: string, index: number) => number +>a : string +>index : number + + return index +>index : number + +}) + +// from https://github.com/microsoft/TypeScript/issues/36307 + +declare class Foo { +>Foo : Foo + + doThing(): Promise +>doThing : () => Promise +} + +declare class Bar extends Foo { +>Bar : Bar +>Foo : Foo + + bar: number; +>bar : number +} +declare class Baz extends Foo { +>Baz : Baz +>Foo : Foo + + baz: number; +>baz : number +} + +declare var a: Bar | Baz; +>a : Bar | Baz + +// note, you must annotate `result` for now +a.doThing().then((result: Bar | Baz) => { +>a.doThing().then((result: Bar | Baz) => { // whatever}) : Promise +>a.doThing().then : ((onfulfilled?: ((value: Bar) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise) | ((onfulfilled?: ((value: Baz) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise) +>a.doThing() : Promise | Promise +>a.doThing : (() => Promise) | (() => Promise) +>a : Bar | Baz +>doThing : (() => Promise) | (() => Promise) +>then : ((onfulfilled?: ((value: Bar) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise) | ((onfulfilled?: ((value: Baz) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise) +>(result: Bar | Baz) => { // whatever} : (result: Bar | Baz) => void +>result : Bar | Baz + + // whatever +}); + diff --git a/tests/cases/compiler/unionOfClassCalls.ts b/tests/cases/compiler/unionOfClassCalls.ts index 28ea00fac1821..509fbb4b46a6c 100644 --- a/tests/cases/compiler/unionOfClassCalls.ts +++ b/tests/cases/compiler/unionOfClassCalls.ts @@ -1,3 +1,5 @@ +// @strict: true +// from https://github.com/microsoft/TypeScript/issues/30717 declare class Test { obj: T; get(k: K): T[K]; @@ -11,4 +13,65 @@ declare const tmp: Test | Test; switch (tmp.get('t')) { case 'A': break; case 'B': break; -} \ No newline at end of file +} + +// from https://github.com/microsoft/TypeScript/issues/36390 + +const arr: number[] | string[] = []; // Works with Array +const arr1: number[] = []; +const arr2: string[] = []; + +arr.map((a: number | string, index: number) => { + return index +}) + +// This case still doesn't work because `reduce` has multiple overloads :( +arr.reduce((acc: Array, a: number | string, index: number) => { + return [] +}, []) + +arr.forEach((a: number | string, index: number) => { + return index +}) + +arr1.map((a: number, index: number) => { + return index +}) + +arr1.reduce((acc: number[], a: number, index: number) => { + return [a] +}, []) + +arr1.forEach((a: number, index: number) => { + return index +}) +arr2.map((a: string, index: number) => { + return index +}) + +arr2.reduce((acc: string[], a: string, index: number) => { + return [] +}, []) + +arr2.forEach((a: string, index: number) => { + return index +}) + +// from https://github.com/microsoft/TypeScript/issues/36307 + +declare class Foo { + doThing(): Promise +} + +declare class Bar extends Foo { + bar: number; +} +declare class Baz extends Foo { + baz: number; +} + +declare var a: Bar | Baz; +// note, you must annotate `result` for now +a.doThing().then((result: Bar | Baz) => { + // whatever +}); From 376a03be7dbd9de1dc4c526d2ac6774979ca764f Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Tue, 15 Dec 2020 15:31:12 -0800 Subject: [PATCH 6/7] Ignore method bivariance in the subtype and strict subtype relationships Fix #41977 --- src/compiler/checker.ts | 11 ++- ...ayLiteralWithMultipleBestCommonTypes.types | 4 +- .../reference/arrayOfFunctionTypes3.types | 14 ++-- .../reference/bestChoiceType.symbols | 18 ++--- .../baselines/reference/bestChoiceType.types | 74 +++++++++---------- .../contextualTypingArrayOfLambdas.types | 4 +- ...tializedDestructuringAssignmentTypes.types | 2 +- .../subtypeReduceBivariance1.errors.txt | 23 ++++++ .../reference/subtypeReduceBivariance1.js | 19 +++++ .../subtypeReduceBivariance1.symbols | 34 +++++++++ .../reference/subtypeReduceBivariance1.types | 31 ++++++++ .../subtypeReduceBivariance2.errors.txt | 23 ++++++ .../reference/subtypeReduceBivariance2.js | 19 +++++ .../subtypeReduceBivariance2.symbols | 34 +++++++++ .../reference/subtypeReduceBivariance2.types | 31 ++++++++ .../subtypeReduceBivarianceChecks.js | 5 ++ .../subtypeReduceBivarianceChecks.symbols | 9 +++ .../subtypeReduceBivarianceChecks.types | 20 +++++ .../subtypingWithCallSignatures3.types | 16 ++-- .../subtypingWithConstructSignatures3.types | 16 ++-- .../reference/tsxUnionElementType3.errors.txt | 12 ++- .../reference/tsxUnionElementType3.types | 8 +- .../reference/tsxUnionElementType4.errors.txt | 7 +- .../reference/tsxUnionElementType4.types | 6 +- .../compiler/subtypeReduceBivariance1.ts | 13 ++++ .../compiler/subtypeReduceBivariance2.ts | 13 ++++ .../compiler/subtypeReduceBivarianceChecks.ts | 1 + 27 files changed, 372 insertions(+), 95 deletions(-) create mode 100644 tests/baselines/reference/subtypeReduceBivariance1.errors.txt create mode 100644 tests/baselines/reference/subtypeReduceBivariance1.js create mode 100644 tests/baselines/reference/subtypeReduceBivariance1.symbols create mode 100644 tests/baselines/reference/subtypeReduceBivariance1.types create mode 100644 tests/baselines/reference/subtypeReduceBivariance2.errors.txt create mode 100644 tests/baselines/reference/subtypeReduceBivariance2.js create mode 100644 tests/baselines/reference/subtypeReduceBivariance2.symbols create mode 100644 tests/baselines/reference/subtypeReduceBivariance2.types create mode 100644 tests/baselines/reference/subtypeReduceBivarianceChecks.js create mode 100644 tests/baselines/reference/subtypeReduceBivarianceChecks.symbols create mode 100644 tests/baselines/reference/subtypeReduceBivarianceChecks.types create mode 100644 tests/cases/compiler/subtypeReduceBivariance1.ts create mode 100644 tests/cases/compiler/subtypeReduceBivariance2.ts create mode 100644 tests/cases/compiler/subtypeReduceBivarianceChecks.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 772fdf9f0bb44..a9840f87e5a8a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -194,6 +194,7 @@ namespace ts { StrictCallback = 1 << 1, IgnoreReturnTypes = 1 << 2, StrictArity = 1 << 3, + IgnoreBivariance = 1 << 4, Callback = BivariantCallback | StrictCallback, } @@ -16280,8 +16281,12 @@ 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 = (checkMode & SignatureCheckMode.IgnoreBivariance) || + (!(checkMode & SignatureCheckMode.Callback) && + strictFunctionTypes && + kind !== SyntaxKind.MethodDeclaration && + kind !== SyntaxKind.MethodSignature && + kind !== SyntaxKind.Constructor); let result = Ternary.True; const sourceThisType = getThisTypeOfSignature(source); @@ -18538,7 +18543,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, isRelatedTo, makeFunctionTypeMapper(reportUnreliableMarkers)); + relation === strictSubtypeRelation ? (SignatureCheckMode.StrictArity | SignatureCheckMode.IgnoreBivariance) : 0, reportErrors, reportError, incompatibleReporter, isRelatedTo, makeFunctionTypeMapper(reportUnreliableMarkers)); } 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: { (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 : (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[] ->(''.match(/ /) || []) : RegExpMatchArray ->''.match(/ /) || [] : RegExpMatchArray +>(''.match(/ /) || []).map(s => s.toLowerCase()) : any[] +>(''.match(/ /) || []).map : ((callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => 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 : (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 : ((callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => 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 : (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[] ->y : RegExpMatchArray ->map : (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 : ((callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | ((callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[]) +>y : RegExpMatchArray | never[] +>map : ((callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => 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 : (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[] ->y : RegExpMatchArray ->map : (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 : ((callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | ((callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[]) +>y : RegExpMatchArray | never[] +>map : ((callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => 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/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/subtypeReduceBivariance1.errors.txt b/tests/baselines/reference/subtypeReduceBivariance1.errors.txt new file mode 100644 index 0000000000000..8b3a215089f78 --- /dev/null +++ b/tests/baselines/reference/subtypeReduceBivariance1.errors.txt @@ -0,0 +1,23 @@ +tests/cases/compiler/subtypeReduceBivariance1.ts(11,51): error TS2322: Type '(a: number) => void' is not assignable to type '(a: string | number) => void'. + Types of parameters 'a' and 'a' are incompatible. + Type 'string | number' is not assignable to type 'number'. + Type 'string' is not assignable to type 'number'. + + +==== tests/cases/compiler/subtypeReduceBivariance1.ts (1 errors) ==== + interface S { + f(a: number | string): void; + } + declare const S: S; + function g(a: number): void { } + + // Force type resolution + g; + S.f; + + const arr: Array<(a: number | string) => void> = [g, S.f]; + ~ +!!! error TS2322: Type '(a: number) => void' is not assignable to type '(a: string | number) => void'. +!!! error TS2322: Types of parameters 'a' and 'a' 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/subtypeReduceBivariance1.js b/tests/baselines/reference/subtypeReduceBivariance1.js new file mode 100644 index 0000000000000..c746528669999 --- /dev/null +++ b/tests/baselines/reference/subtypeReduceBivariance1.js @@ -0,0 +1,19 @@ +//// [subtypeReduceBivariance1.ts] +interface S { + f(a: number | string): void; +} +declare const S: S; +function g(a: number): void { } + +// Force type resolution +g; +S.f; + +const arr: Array<(a: number | string) => void> = [g, S.f]; + +//// [subtypeReduceBivariance1.js] +function g(a) { } +// Force type resolution +g; +S.f; +var arr = [g, S.f]; diff --git a/tests/baselines/reference/subtypeReduceBivariance1.symbols b/tests/baselines/reference/subtypeReduceBivariance1.symbols new file mode 100644 index 0000000000000..84e16b94d7137 --- /dev/null +++ b/tests/baselines/reference/subtypeReduceBivariance1.symbols @@ -0,0 +1,34 @@ +=== tests/cases/compiler/subtypeReduceBivariance1.ts === +interface S { +>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13)) + + f(a: number | string): void; +>f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13)) +>a : Symbol(a, Decl(subtypeReduceBivariance1.ts, 1, 6)) +} +declare const S: S; +>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13)) +>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13)) + +function g(a: number): void { } +>g : Symbol(g, Decl(subtypeReduceBivariance1.ts, 3, 19)) +>a : Symbol(a, Decl(subtypeReduceBivariance1.ts, 4, 11)) + +// Force type resolution +g; +>g : Symbol(g, Decl(subtypeReduceBivariance1.ts, 3, 19)) + +S.f; +>S.f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13)) +>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13)) +>f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13)) + +const arr: Array<(a: number | string) => void> = [g, S.f]; +>arr : Symbol(arr, Decl(subtypeReduceBivariance1.ts, 10, 5)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(subtypeReduceBivariance1.ts, 10, 18)) +>g : Symbol(g, Decl(subtypeReduceBivariance1.ts, 3, 19)) +>S.f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13)) +>S : Symbol(S, Decl(subtypeReduceBivariance1.ts, 0, 0), Decl(subtypeReduceBivariance1.ts, 3, 13)) +>f : Symbol(S.f, Decl(subtypeReduceBivariance1.ts, 0, 13)) + diff --git a/tests/baselines/reference/subtypeReduceBivariance1.types b/tests/baselines/reference/subtypeReduceBivariance1.types new file mode 100644 index 0000000000000..27d44e1960f78 --- /dev/null +++ b/tests/baselines/reference/subtypeReduceBivariance1.types @@ -0,0 +1,31 @@ +=== tests/cases/compiler/subtypeReduceBivariance1.ts === +interface S { + f(a: number | string): void; +>f : (a: number | string) => void +>a : string | number +} +declare const S: S; +>S : S + +function g(a: number): void { } +>g : (a: number) => void +>a : number + +// Force type resolution +g; +>g : (a: number) => void + +S.f; +>S.f : (a: string | number) => void +>S : S +>f : (a: string | number) => void + +const arr: Array<(a: number | string) => void> = [g, S.f]; +>arr : ((a: number | string) => void)[] +>a : string | number +>[g, S.f] : ((a: number) => void)[] +>g : (a: number) => void +>S.f : (a: string | number) => void +>S : S +>f : (a: string | number) => void + diff --git a/tests/baselines/reference/subtypeReduceBivariance2.errors.txt b/tests/baselines/reference/subtypeReduceBivariance2.errors.txt new file mode 100644 index 0000000000000..d5e0d85ea85eb --- /dev/null +++ b/tests/baselines/reference/subtypeReduceBivariance2.errors.txt @@ -0,0 +1,23 @@ +tests/cases/compiler/subtypeReduceBivariance2.ts(11,51): error TS2322: Type '(a: number) => void' is not assignable to type '(a: string | number) => void'. + Types of parameters 'a' and 'a' are incompatible. + Type 'string | number' is not assignable to type 'number'. + Type 'string' is not assignable to type 'number'. + + +==== tests/cases/compiler/subtypeReduceBivariance2.ts (1 errors) ==== + interface S { + f(a: number | string): void; + } + declare const S: S; + function g(a: number): void { } + + // Force type resolution + S.f; + g; + + const arr: Array<(a: number | string) => void> = [g, S.f]; + ~ +!!! error TS2322: Type '(a: number) => void' is not assignable to type '(a: string | number) => void'. +!!! error TS2322: Types of parameters 'a' and 'a' 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/subtypeReduceBivariance2.js b/tests/baselines/reference/subtypeReduceBivariance2.js new file mode 100644 index 0000000000000..54e79418c92bf --- /dev/null +++ b/tests/baselines/reference/subtypeReduceBivariance2.js @@ -0,0 +1,19 @@ +//// [subtypeReduceBivariance2.ts] +interface S { + f(a: number | string): void; +} +declare const S: S; +function g(a: number): void { } + +// Force type resolution +S.f; +g; + +const arr: Array<(a: number | string) => void> = [g, S.f]; + +//// [subtypeReduceBivariance2.js] +function g(a) { } +// Force type resolution +S.f; +g; +var arr = [g, S.f]; diff --git a/tests/baselines/reference/subtypeReduceBivariance2.symbols b/tests/baselines/reference/subtypeReduceBivariance2.symbols new file mode 100644 index 0000000000000..5247d235dba3c --- /dev/null +++ b/tests/baselines/reference/subtypeReduceBivariance2.symbols @@ -0,0 +1,34 @@ +=== tests/cases/compiler/subtypeReduceBivariance2.ts === +interface S { +>S : Symbol(S, Decl(subtypeReduceBivariance2.ts, 0, 0), Decl(subtypeReduceBivariance2.ts, 3, 13)) + + f(a: number | string): void; +>f : Symbol(S.f, Decl(subtypeReduceBivariance2.ts, 0, 13)) +>a : Symbol(a, Decl(subtypeReduceBivariance2.ts, 1, 6)) +} +declare const S: S; +>S : Symbol(S, Decl(subtypeReduceBivariance2.ts, 0, 0), Decl(subtypeReduceBivariance2.ts, 3, 13)) +>S : Symbol(S, Decl(subtypeReduceBivariance2.ts, 0, 0), Decl(subtypeReduceBivariance2.ts, 3, 13)) + +function g(a: number): void { } +>g : Symbol(g, Decl(subtypeReduceBivariance2.ts, 3, 19)) +>a : Symbol(a, Decl(subtypeReduceBivariance2.ts, 4, 11)) + +// Force type resolution +S.f; +>S.f : Symbol(S.f, Decl(subtypeReduceBivariance2.ts, 0, 13)) +>S : Symbol(S, Decl(subtypeReduceBivariance2.ts, 0, 0), Decl(subtypeReduceBivariance2.ts, 3, 13)) +>f : Symbol(S.f, Decl(subtypeReduceBivariance2.ts, 0, 13)) + +g; +>g : Symbol(g, Decl(subtypeReduceBivariance2.ts, 3, 19)) + +const arr: Array<(a: number | string) => void> = [g, S.f]; +>arr : Symbol(arr, Decl(subtypeReduceBivariance2.ts, 10, 5)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(subtypeReduceBivariance2.ts, 10, 18)) +>g : Symbol(g, Decl(subtypeReduceBivariance2.ts, 3, 19)) +>S.f : Symbol(S.f, Decl(subtypeReduceBivariance2.ts, 0, 13)) +>S : Symbol(S, Decl(subtypeReduceBivariance2.ts, 0, 0), Decl(subtypeReduceBivariance2.ts, 3, 13)) +>f : Symbol(S.f, Decl(subtypeReduceBivariance2.ts, 0, 13)) + diff --git a/tests/baselines/reference/subtypeReduceBivariance2.types b/tests/baselines/reference/subtypeReduceBivariance2.types new file mode 100644 index 0000000000000..78e4ec5c1e4f1 --- /dev/null +++ b/tests/baselines/reference/subtypeReduceBivariance2.types @@ -0,0 +1,31 @@ +=== tests/cases/compiler/subtypeReduceBivariance2.ts === +interface S { + f(a: number | string): void; +>f : (a: number | string) => void +>a : string | number +} +declare const S: S; +>S : S + +function g(a: number): void { } +>g : (a: number) => void +>a : number + +// Force type resolution +S.f; +>S.f : (a: string | number) => void +>S : S +>f : (a: string | number) => void + +g; +>g : (a: number) => void + +const arr: Array<(a: number | string) => void> = [g, S.f]; +>arr : ((a: number | string) => void)[] +>a : string | number +>[g, S.f] : ((a: number) => void)[] +>g : (a: number) => void +>S.f : (a: string | number) => void +>S : S +>f : (a: string | number) => void + diff --git a/tests/baselines/reference/subtypeReduceBivarianceChecks.js b/tests/baselines/reference/subtypeReduceBivarianceChecks.js new file mode 100644 index 0000000000000..f1929cac5cfe1 --- /dev/null +++ b/tests/baselines/reference/subtypeReduceBivarianceChecks.js @@ -0,0 +1,5 @@ +//// [subtypeReduceBivarianceChecks.ts] +(''.match(/ /) || []).map(s => s.toLowerCase()); + +//// [subtypeReduceBivarianceChecks.js] +(''.match(/ /) || []).map(function (s) { return s.toLowerCase(); }); diff --git a/tests/baselines/reference/subtypeReduceBivarianceChecks.symbols b/tests/baselines/reference/subtypeReduceBivarianceChecks.symbols new file mode 100644 index 0000000000000..eb467c9f8da6f --- /dev/null +++ b/tests/baselines/reference/subtypeReduceBivarianceChecks.symbols @@ -0,0 +1,9 @@ +=== tests/cases/compiler/subtypeReduceBivarianceChecks.ts === +(''.match(/ /) || []).map(s => s.toLowerCase()); +>(''.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, --, --), Decl(lib.es5.d.ts, --, --)) +>s : Symbol(s, Decl(subtypeReduceBivarianceChecks.ts, 0, 26)) +>s : Symbol(s, Decl(subtypeReduceBivarianceChecks.ts, 0, 26)) + diff --git a/tests/baselines/reference/subtypeReduceBivarianceChecks.types b/tests/baselines/reference/subtypeReduceBivarianceChecks.types new file mode 100644 index 0000000000000..86a401d051aec --- /dev/null +++ b/tests/baselines/reference/subtypeReduceBivarianceChecks.types @@ -0,0 +1,20 @@ +=== tests/cases/compiler/subtypeReduceBivarianceChecks.ts === +(''.match(/ /) || []).map(s => s.toLowerCase()); +>(''.match(/ /) || []).map(s => s.toLowerCase()) : any[] +>(''.match(/ /) || []).map : ((callbackfn: (value: any, index: number, array: any[]) => U, thisArg?: any) => U[]) | ((callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) +>(''.match(/ /) || []) : RegExpMatchArray | undefined[] +>''.match(/ /) || [] : RegExpMatchArray | undefined[] +>''.match(/ /) : RegExpMatchArray +>''.match : (regexp: string | RegExp) => RegExpMatchArray +>'' : "" +>match : (regexp: string | RegExp) => RegExpMatchArray +>/ / : RegExp +>[] : undefined[] +>map : ((callbackfn: (value: any, index: number, array: any[]) => U, thisArg?: any) => U[]) | ((callbackfn: (value: string, index: number, array: string[]) => 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/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 : (...x: T[]) => T var r4a = [r4arg2, r4arg]; ->r4a : ((...x: Base[]) => Base)[] ->[r4arg2, r4arg] : ((...x: Base[]) => Base)[] +>r4a : (((...x: T[]) => T) | ((...x: Base[]) => Base))[] +>[r4arg2, r4arg] : (((...x: T[]) => T) | ((...x: Base[]) => Base))[] >r4arg2 : (...x: Base[]) => Base >r4arg : (...x: T[]) => T var r4b = [r4arg, r4arg2]; ->r4b : ((...x: Base[]) => Base)[] ->[r4arg, r4arg2] : ((...x: Base[]) => Base)[] +>r4b : (((...x: T[]) => T) | ((...x: Base[]) => Base))[] +>[r4arg, r4arg2] : (((...x: T[]) => T) | ((...x: Base[]) => Base))[] >r4arg : (...x: T[]) => T >r4arg2 : (...x: Base[]) => Base @@ -342,14 +342,14 @@ module Errors { >r5arg : (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 : (((x: T, y: T) => T) | ((x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[] +>[r5arg2, r5arg] : (((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 : (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 : (((x: T, y: T) => T) | ((x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[] +>[r5arg, r5arg2] : (((x: T, y: T) => T) | ((x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[] >r5arg : (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 (...x: T[]) => T var r4a = [r4arg2, r4arg1]; ->r4a : (new (...x: Base[]) => Base)[] ->[r4arg2, r4arg1] : (new (...x: Base[]) => Base)[] +>r4a : ((new (...x: T[]) => T) | (new (...x: Base[]) => Base))[] +>[r4arg2, r4arg1] : ((new (...x: T[]) => T) | (new (...x: Base[]) => Base))[] >r4arg2 : new (...x: Base[]) => Base >r4arg1 : new (...x: T[]) => T var r4b = [r4arg1, r4arg2]; ->r4b : (new (...x: Base[]) => Base)[] ->[r4arg1, r4arg2] : (new (...x: Base[]) => Base)[] +>r4b : ((new (...x: T[]) => T) | (new (...x: Base[]) => Base))[] +>[r4arg1, r4arg2] : ((new (...x: T[]) => T) | (new (...x: Base[]) => Base))[] >r4arg1 : new (...x: T[]) => T >r4arg2 : new (...x: Base[]) => Base @@ -307,14 +307,14 @@ module Errors { >r5arg1 : new (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 (x: T, y: T) => T) | (new (x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[] +>[r5arg2, r5arg1] : ((new (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 (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 (x: T, y: T) => T) | (new (x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[] +>[r5arg1, r5arg2] : ((new (x: T, y: T) => T) | (new (x: { foo: string; }, y: { foo: string; bar: string; }) => Base))[] >r5arg1 : new (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 = ; let a2 = ; let b = - let c = \ 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 = + ~~~~~~~~~~ +!!! 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 = ; let b = >b : JSX.Element > : JSX.Element ->PartRCComp : typeof RC4 +>PartRCComp : typeof RC1 | typeof RC4 let c = >c : JSX.Element > : 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 & { children?: ReactNode; }'. - Property 'x' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & { children?: ReactNode; }'. tests/cases/conformance/jsx/file.tsx(34,22): error TS2322: Type '{ prop: true; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & { children?: ReactNode; }'. Property 'prop' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & { 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 & { x: number; } & { children?: ReactNode; } & { x: string; } & { children?: ReactNode; }' let b = - ~ -!!! error TS2322: Type '{ x: number; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & { children?: ReactNode; }'. -!!! error TS2322: Property 'x' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & { children?: ReactNode; }'. let c = ; ~~~~ !!! error TS2322: Type '{ prop: true; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & { 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 = ; let b = >b : JSX.Element > : JSX.Element ->PartRCComp : typeof RC4 +>PartRCComp : typeof RC1 | typeof RC4 >x : number >10 : 10 diff --git a/tests/cases/compiler/subtypeReduceBivariance1.ts b/tests/cases/compiler/subtypeReduceBivariance1.ts new file mode 100644 index 0000000000000..20d3901e56b57 --- /dev/null +++ b/tests/cases/compiler/subtypeReduceBivariance1.ts @@ -0,0 +1,13 @@ +//@strictFunctionTypes: true + +interface S { + f(a: number | string): void; +} +declare const S: S; +function g(a: number): void { } + +// Force type resolution +g; +S.f; + +const arr: Array<(a: number | string) => void> = [g, S.f]; \ No newline at end of file diff --git a/tests/cases/compiler/subtypeReduceBivariance2.ts b/tests/cases/compiler/subtypeReduceBivariance2.ts new file mode 100644 index 0000000000000..dfa4fa09c2161 --- /dev/null +++ b/tests/cases/compiler/subtypeReduceBivariance2.ts @@ -0,0 +1,13 @@ +//@strictFunctionTypes: true + +interface S { + f(a: number | string): void; +} +declare const S: S; +function g(a: number): void { } + +// Force type resolution +S.f; +g; + +const arr: Array<(a: number | string) => void> = [g, S.f]; \ No newline at end of file diff --git a/tests/cases/compiler/subtypeReduceBivarianceChecks.ts b/tests/cases/compiler/subtypeReduceBivarianceChecks.ts new file mode 100644 index 0000000000000..4f91e0f864b85 --- /dev/null +++ b/tests/cases/compiler/subtypeReduceBivarianceChecks.ts @@ -0,0 +1 @@ +(''.match(/ /) || []).map(s => s.toLowerCase()); \ No newline at end of file From 7479b8a31a7472deb858f1f546734b6d9bc6fc6b Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Wed, 16 Dec 2020 10:44:03 -0800 Subject: [PATCH 7/7] Fix build break --- src/compiler/visitorPublic.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index ad6e45f401184..8854b4b44421f 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -36,7 +36,7 @@ namespace ts { return undefined; } else if (isArray(visited)) { - visitedNode = (lift || extractSingleNode)(visited); + visitedNode = (lift || extractSingleNode)(visited as unknown as NodeArray); } else { visitedNode = visited;