From ed495942d5b1cf0d0aa7c9d05291008641e0b7f4 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 23 Jun 2020 15:58:10 -0700 Subject: [PATCH 1/2] Erase signatures to a type which behaves as never in a union and unknown in an intersection or any otherwise --- src/compiler/checker.ts | 7 +- .../complexRecursiveCollections.errors.txt | 9 ++- .../complexRecursiveCollections.types | 2 +- ...functionInferenceDecomposesIntersection.js | 33 ++++++++++ ...ionInferenceDecomposesIntersection.symbols | 66 +++++++++++++++++++ ...ctionInferenceDecomposesIntersection.types | 43 ++++++++++++ .../subtypingWithCallSignatures2.types | 8 +-- .../subtypingWithCallSignatures3.types | 4 +- .../subtypingWithConstructSignatures2.types | 8 +-- .../subtypingWithConstructSignatures3.types | 4 +- ...functionInferenceDecomposesIntersection.ts | 25 +++++++ 11 files changed, 192 insertions(+), 17 deletions(-) create mode 100644 tests/baselines/reference/functionInferenceDecomposesIntersection.js create mode 100644 tests/baselines/reference/functionInferenceDecomposesIntersection.symbols create mode 100644 tests/baselines/reference/functionInferenceDecomposesIntersection.types create mode 100644 tests/cases/compiler/functionInferenceDecomposesIntersection.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 61fa4d1c69e2f..89875aaebcde9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -700,6 +700,7 @@ namespace ts { const anyType = createIntrinsicType(TypeFlags.Any, "any"); const autoType = createIntrinsicType(TypeFlags.Any, "any"); const wildcardType = createIntrinsicType(TypeFlags.Any, "any"); + const erasedType = createIntrinsicType(TypeFlags.Any | TypeFlags.Never | TypeFlags.Unknown, "any"); const errorType = createIntrinsicType(TypeFlags.Any, "error"); const nonInferrableAnyType = createIntrinsicType(TypeFlags.Any, "any", ObjectFlags.ContainsWideningType); const unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown"); @@ -12896,8 +12897,8 @@ namespace ts { typeSet.set(type.id.toString(), type); } } - else { - if (flags & TypeFlags.AnyOrUnknown) { + else if (!(flags & TypeFlags.Unknown)) { + if (flags & TypeFlags.Any) { if (type === wildcardType) includes |= TypeFlags.IncludesWildcard; } else if ((strictNullChecks || !(flags & TypeFlags.Nullable)) && !typeSet.has(type.id.toString())) { @@ -14461,7 +14462,7 @@ namespace ts { } function createTypeEraser(sources: readonly TypeParameter[]): TypeMapper { - return createTypeMapper(sources, /*targets*/ undefined); + return createTypeMapper(sources, arrayOf(sources.length, () => erasedType)); } /** diff --git a/tests/baselines/reference/complexRecursiveCollections.errors.txt b/tests/baselines/reference/complexRecursiveCollections.errors.txt index 91745b84e384d..502e380d93731 100644 --- a/tests/baselines/reference/complexRecursiveCollections.errors.txt +++ b/tests/baselines/reference/complexRecursiveCollections.errors.txt @@ -1,3 +1,6 @@ +tests/cases/compiler/immutable.ts(189,20): error TS2430: Interface 'Stack' incorrectly extends interface 'Indexed'. + The types returned by 'concat(...).map(...).filter(...)' are incompatible between these types. + Type 'Set' is not assignable to type 'Indexed'. tests/cases/compiler/immutable.ts(341,22): error TS2430: Interface 'Keyed' incorrectly extends interface 'Collection'. The types returned by 'toSeq()' are incompatible between these types. Type 'Keyed' is not assignable to type 'this'. @@ -33,7 +36,7 @@ tests/cases/compiler/immutable.ts(391,22): error TS2430: Interface 'Set' inco flatMap(mapper: (value: T, key: void, iter: this) => Ara, context?: any): N2; toSeq(): N2; } -==== tests/cases/compiler/immutable.ts (3 errors) ==== +==== tests/cases/compiler/immutable.ts (4 errors) ==== // Test that complex recursive collections can pass the `extends` assignability check without // running out of memory. This bug was exposed in Typescript 2.4 when more generic signatures // started being checked. @@ -223,6 +226,10 @@ tests/cases/compiler/immutable.ts(391,22): error TS2430: Interface 'Set' inco export function Stack(): Stack; export function Stack(collection: Iterable): Stack; export interface Stack extends Collection.Indexed { + ~~~~~ +!!! error TS2430: Interface 'Stack' incorrectly extends interface 'Indexed'. +!!! error TS2430: The types returned by 'concat(...).map(...).filter(...)' are incompatible between these types. +!!! error TS2430: Type 'Set' is not assignable to type 'Indexed'. // Reading values peek(): T | undefined; // Persistent changes diff --git a/tests/baselines/reference/complexRecursiveCollections.types b/tests/baselines/reference/complexRecursiveCollections.types index 783bc32015ae8..951e6591fc476 100644 --- a/tests/baselines/reference/complexRecursiveCollections.types +++ b/tests/baselines/reference/complexRecursiveCollections.types @@ -1137,7 +1137,7 @@ declare module Immutable { >Seq : typeof Seq function isSeq(maybeSeq: any): maybeSeq is Seq.Indexed | Seq.Keyed; ->isSeq : (maybeSeq: any) => maybeSeq is Indexed | Keyed +>isSeq : (maybeSeq: any) => maybeSeq is Keyed | Indexed >maybeSeq : any >Seq : any >Seq : any diff --git a/tests/baselines/reference/functionInferenceDecomposesIntersection.js b/tests/baselines/reference/functionInferenceDecomposesIntersection.js new file mode 100644 index 0000000000000..36717d6df8cee --- /dev/null +++ b/tests/baselines/reference/functionInferenceDecomposesIntersection.js @@ -0,0 +1,33 @@ +//// [functionInferenceDecomposesIntersection.ts] +declare namespace React { + type WeakValidationMap = { + [K in keyof T]?: null extends T[K] ? string : string + }; + + interface FunctionComponent

{ + propTypes?: WeakValidationMap

; + } +} + +type A = () => React.FunctionComponent; + +function B(_: A) {} + +interface C { + r: string; +} + +function myFunction(): React.FunctionComponent { + return {}; +} + +// B(myFunction) // No error +B(myFunction) // should be the same as above (T in B inferred as C) + +//// [functionInferenceDecomposesIntersection.js] +function B(_) { } +function myFunction() { + return {}; +} +// B(myFunction) // No error +B(myFunction); // should be the same as above (T in B inferred as C) diff --git a/tests/baselines/reference/functionInferenceDecomposesIntersection.symbols b/tests/baselines/reference/functionInferenceDecomposesIntersection.symbols new file mode 100644 index 0000000000000..767f53d88af71 --- /dev/null +++ b/tests/baselines/reference/functionInferenceDecomposesIntersection.symbols @@ -0,0 +1,66 @@ +=== tests/cases/compiler/functionInferenceDecomposesIntersection.ts === +declare namespace React { +>React : Symbol(React, Decl(functionInferenceDecomposesIntersection.ts, 0, 0)) + + type WeakValidationMap = { +>WeakValidationMap : Symbol(WeakValidationMap, Decl(functionInferenceDecomposesIntersection.ts, 0, 25)) +>T : Symbol(T, Decl(functionInferenceDecomposesIntersection.ts, 1, 27)) + + [K in keyof T]?: null extends T[K] ? string : string +>K : Symbol(K, Decl(functionInferenceDecomposesIntersection.ts, 2, 9)) +>T : Symbol(T, Decl(functionInferenceDecomposesIntersection.ts, 1, 27)) +>T : Symbol(T, Decl(functionInferenceDecomposesIntersection.ts, 1, 27)) +>K : Symbol(K, Decl(functionInferenceDecomposesIntersection.ts, 2, 9)) + + }; + + interface FunctionComponent

{ +>FunctionComponent : Symbol(FunctionComponent, Decl(functionInferenceDecomposesIntersection.ts, 3, 6)) +>P : Symbol(P, Decl(functionInferenceDecomposesIntersection.ts, 5, 32)) + + propTypes?: WeakValidationMap

; +>propTypes : Symbol(FunctionComponent.propTypes, Decl(functionInferenceDecomposesIntersection.ts, 5, 41)) +>WeakValidationMap : Symbol(WeakValidationMap, Decl(functionInferenceDecomposesIntersection.ts, 0, 25)) +>P : Symbol(P, Decl(functionInferenceDecomposesIntersection.ts, 5, 32)) + } +} + +type A = () => React.FunctionComponent; +>A : Symbol(A, Decl(functionInferenceDecomposesIntersection.ts, 8, 1)) +>T1 : Symbol(T1, Decl(functionInferenceDecomposesIntersection.ts, 10, 7)) +>T2 : Symbol(T2, Decl(functionInferenceDecomposesIntersection.ts, 10, 14)) +>React : Symbol(React, Decl(functionInferenceDecomposesIntersection.ts, 0, 0)) +>FunctionComponent : Symbol(React.FunctionComponent, Decl(functionInferenceDecomposesIntersection.ts, 3, 6)) +>T1 : Symbol(T1, Decl(functionInferenceDecomposesIntersection.ts, 10, 7)) +>T2 : Symbol(T2, Decl(functionInferenceDecomposesIntersection.ts, 10, 14)) + +function B(_: A) {} +>B : Symbol(B, Decl(functionInferenceDecomposesIntersection.ts, 10, 56)) +>T : Symbol(T, Decl(functionInferenceDecomposesIntersection.ts, 12, 11)) +>_ : Symbol(_, Decl(functionInferenceDecomposesIntersection.ts, 12, 14)) +>A : Symbol(A, Decl(functionInferenceDecomposesIntersection.ts, 8, 1)) +>T : Symbol(T, Decl(functionInferenceDecomposesIntersection.ts, 12, 11)) + +interface C { +>C : Symbol(C, Decl(functionInferenceDecomposesIntersection.ts, 12, 25)) + + r: string; +>r : Symbol(C.r, Decl(functionInferenceDecomposesIntersection.ts, 14, 13)) +} + +function myFunction(): React.FunctionComponent { +>myFunction : Symbol(myFunction, Decl(functionInferenceDecomposesIntersection.ts, 16, 1)) +>T2 : Symbol(T2, Decl(functionInferenceDecomposesIntersection.ts, 18, 20)) +>React : Symbol(React, Decl(functionInferenceDecomposesIntersection.ts, 0, 0)) +>FunctionComponent : Symbol(React.FunctionComponent, Decl(functionInferenceDecomposesIntersection.ts, 3, 6)) +>C : Symbol(C, Decl(functionInferenceDecomposesIntersection.ts, 12, 25)) +>T2 : Symbol(T2, Decl(functionInferenceDecomposesIntersection.ts, 18, 20)) + + return {}; +} + +// B(myFunction) // No error +B(myFunction) // should be the same as above (T in B inferred as C) +>B : Symbol(B, Decl(functionInferenceDecomposesIntersection.ts, 10, 56)) +>myFunction : Symbol(myFunction, Decl(functionInferenceDecomposesIntersection.ts, 16, 1)) + diff --git a/tests/baselines/reference/functionInferenceDecomposesIntersection.types b/tests/baselines/reference/functionInferenceDecomposesIntersection.types new file mode 100644 index 0000000000000..8bbfc22e54e56 --- /dev/null +++ b/tests/baselines/reference/functionInferenceDecomposesIntersection.types @@ -0,0 +1,43 @@ +=== tests/cases/compiler/functionInferenceDecomposesIntersection.ts === +declare namespace React { + type WeakValidationMap = { +>WeakValidationMap : WeakValidationMap + + [K in keyof T]?: null extends T[K] ? string : string +>null : null + + }; + + interface FunctionComponent

{ + propTypes?: WeakValidationMap

; +>propTypes : WeakValidationMap

+ } +} + +type A = () => React.FunctionComponent; +>A : A +>React : any + +function B(_: A) {} +>B : (_: A) => void +>_ : A + +interface C { + r: string; +>r : string +} + +function myFunction(): React.FunctionComponent { +>myFunction : () => React.FunctionComponent +>React : any + + return {}; +>{} : {} +} + +// B(myFunction) // No error +B(myFunction) // should be the same as above (T in B inferred as C) +>B(myFunction) : void +>B : (_: A) => void +>myFunction : () => React.FunctionComponent + diff --git a/tests/baselines/reference/subtypingWithCallSignatures2.types b/tests/baselines/reference/subtypingWithCallSignatures2.types index 51bdbc5ed9834..cd880fc9c233a 100644 --- a/tests/baselines/reference/subtypingWithCallSignatures2.types +++ b/tests/baselines/reference/subtypingWithCallSignatures2.types @@ -762,8 +762,8 @@ var r15arg1 = (x: T) => null >null : null var r15 = foo15(r15arg1); // any ->r15 : any ->foo15(r15arg1) : any +>r15 : { (x: number): number[]; (x: string): string[]; } +>foo15(r15arg1) : { (x: number): number[]; (x: string): string[]; } >foo15 : { (a: { (x: number): number[]; (x: string): string[]; }): { (x: number): number[]; (x: string): string[]; }; (a: any): any; } >r15arg1 : (x: T) => T[] @@ -789,8 +789,8 @@ var r17arg1 = (x: (a: T) => T) => null; >null : null var r17 = foo17(r17arg1); // any ->r17 : any ->foo17(r17arg1) : any +>r17 : { (x: (a: number) => number): number[]; (x: (a: string) => string): string[]; } +>foo17(r17arg1) : { (x: (a: number) => number): number[]; (x: (a: string) => string): string[]; } >foo17 : { (a: { (x: (a: number) => number): number[]; (x: (a: string) => string): string[]; }): { (x: (a: number) => number): number[]; (x: (a: string) => string): string[]; }; (a: any): any; } >r17arg1 : (x: (a: T) => T) => T[] diff --git a/tests/baselines/reference/subtypingWithCallSignatures3.types b/tests/baselines/reference/subtypingWithCallSignatures3.types index 7116d132b7d2b..0e1c89303704e 100644 --- a/tests/baselines/reference/subtypingWithCallSignatures3.types +++ b/tests/baselines/reference/subtypingWithCallSignatures3.types @@ -457,8 +457,8 @@ module Errors { >null : null var r8 = foo16(r8arg); // any ->r8 : any ->foo16(r8arg) : any +>r8 : { (x: { (a: number): number; (a?: number): number; }): number[]; (x: { (a: boolean): boolean; (a?: boolean): boolean; }): boolean[]; } +>foo16(r8arg) : { (x: { (a: number): number; (a?: number): number; }): number[]; (x: { (a: boolean): boolean; (a?: boolean): boolean; }): boolean[]; } >foo16 : { (a2: { (x: { (a: number): number; (a?: number): number; }): number[]; (x: { (a: boolean): boolean; (a?: boolean): boolean; }): boolean[]; }): { (x: { (a: number): number; (a?: number): number; }): number[]; (x: { (a: boolean): boolean; (a?: boolean): boolean; }): boolean[]; }; (a2: any): any; } >r8arg : (x: (a: T) => T) => T[] diff --git a/tests/baselines/reference/subtypingWithConstructSignatures2.types b/tests/baselines/reference/subtypingWithConstructSignatures2.types index ef65ae549c7c2..d11cd4ca7be8a 100644 --- a/tests/baselines/reference/subtypingWithConstructSignatures2.types +++ b/tests/baselines/reference/subtypingWithConstructSignatures2.types @@ -675,8 +675,8 @@ var r15arg1: new (x: T) => T[]; >x : T var r15 = foo15(r15arg1); // any ->r15 : any ->foo15(r15arg1) : any +>r15 : { new (x: number): number[]; new (x: string): string[]; } +>foo15(r15arg1) : { new (x: number): number[]; new (x: string): string[]; } >foo15 : { (a: { new (x: number): number[]; new (x: string): string[]; }): { new (x: number): number[]; new (x: string): string[]; }; (a: any): any; } >r15arg1 : new (x: T) => T[] @@ -696,8 +696,8 @@ var r17arg1: new (x: (a: T) => T) => T[]; >a : T var r17 = foo17(r17arg1); // any ->r17 : any ->foo17(r17arg1) : any +>r17 : { new (x: (a: number) => number): number[]; new (x: (a: string) => string): string[]; } +>foo17(r17arg1) : { new (x: (a: number) => number): number[]; new (x: (a: string) => string): string[]; } >foo17 : { (a: { new (x: (a: number) => number): number[]; new (x: (a: string) => string): string[]; }): { new (x: (a: number) => number): number[]; new (x: (a: string) => string): string[]; }; (a: any): any; } >r17arg1 : new (x: (a: T) => T) => T[] diff --git a/tests/baselines/reference/subtypingWithConstructSignatures3.types b/tests/baselines/reference/subtypingWithConstructSignatures3.types index 5cf176caa0934..20ddd23c552ba 100644 --- a/tests/baselines/reference/subtypingWithConstructSignatures3.types +++ b/tests/baselines/reference/subtypingWithConstructSignatures3.types @@ -406,8 +406,8 @@ module Errors { >a : T var r8 = foo16(r8arg); // any ->r8 : any ->foo16(r8arg) : any +>r8 : { new (x: { new (a: number): number; new (a?: number): number; }): number[]; new (x: { new (a: boolean): boolean; new (a?: boolean): boolean; }): boolean[]; } +>foo16(r8arg) : { new (x: { new (a: number): number; new (a?: number): number; }): number[]; new (x: { new (a: boolean): boolean; new (a?: boolean): boolean; }): boolean[]; } >foo16 : { (a2: { new (x: { new (a: number): number; new (a?: number): number; }): number[]; new (x: { new (a: boolean): boolean; new (a?: boolean): boolean; }): boolean[]; }): { new (x: { new (a: number): number; new (a?: number): number; }): number[]; new (x: { new (a: boolean): boolean; new (a?: boolean): boolean; }): boolean[]; }; (a2: any): any; } >r8arg : new (x: new (a: T) => T) => T[] diff --git a/tests/cases/compiler/functionInferenceDecomposesIntersection.ts b/tests/cases/compiler/functionInferenceDecomposesIntersection.ts new file mode 100644 index 0000000000000..4ead21e472720 --- /dev/null +++ b/tests/cases/compiler/functionInferenceDecomposesIntersection.ts @@ -0,0 +1,25 @@ + +declare namespace React { + type WeakValidationMap = { + [K in keyof T]?: null extends T[K] ? string : string + }; + + interface FunctionComponent

{ + propTypes?: WeakValidationMap

; + } +} + +type A = () => React.FunctionComponent; + +function B(_: A) {} + +interface C { + r: string; +} + +function myFunction(): React.FunctionComponent { + return {}; +} + +// B(myFunction) // No error +B(myFunction) // should be the same as above (T in B inferred as C) \ No newline at end of file From c084d2825a8c578b88e8523d9ff92e3d129c1c57 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 24 Jun 2020 08:53:55 -0700 Subject: [PATCH 2/2] Specifically handle erasedType as a special any in getIndexedAccessType --- src/compiler/checker.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 89875aaebcde9..cc3ac04be8d95 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13605,6 +13605,9 @@ namespace ts { if (objectType === wildcardType || indexType === wildcardType) { return wildcardType; } + if (objectType === erasedType || indexType === erasedType) { + return erasedType; + } // If the object type has a string index signature and no other members we know that the result will // always be the type of that index signature and we can simplify accordingly. if (isStringIndexSignatureOnlyType(objectType) && !(indexType.flags & TypeFlags.Nullable) && isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) {