From cecab849dbe79e3fd525e1f7cd262f749b39980a Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 23 Mar 2020 13:32:19 -0700 Subject: [PATCH 1/4] Fix inference of type arguments when source is 'awaited' --- src/compiler/checker.ts | 38 +++++++++++++++---- src/compiler/sys.ts | 2 +- src/compiler/types.ts | 6 ++- tests/baselines/reference/awaitedInference.js | 11 +++++- .../reference/awaitedInference.symbols | 23 +++++++++-- .../reference/awaitedInference.types | 14 +++++++ .../types/awaited/awaitedInference.ts | 9 ++++- 7 files changed, 86 insertions(+), 17 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8e1235f845260..45f66a5e3cea4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -929,7 +929,7 @@ namespace ts { let _jsxNamespace: __String; let _jsxFactoryEntity: EntityName | undefined; - let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined; + let outofbandVarianceMarkerHandler: ((variance: VarianceFlags) => void) | undefined; const subtypeRelation = createMap(); const strictSubtypeRelation = createMap(); @@ -15797,6 +15797,9 @@ namespace ts { if (saved & RelationComparisonResult.ReportsUnreliable) { instantiateType(source, makeFunctionTypeMapper(reportUnreliableMarkers)); } + if (saved & RelationComparisonResult.ReportsAwaited) { + instantiateType(source, makeFunctionTypeMapper(reportAwaitedMarkers)); + } } return entry & RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False; } @@ -15831,10 +15834,12 @@ namespace ts { let propagatingVarianceFlags: RelationComparisonResult = 0; if (outofbandVarianceMarkerHandler) { originalHandler = outofbandVarianceMarkerHandler; - outofbandVarianceMarkerHandler = onlyUnreliable => { + outofbandVarianceMarkerHandler = variance => { propagatingVarianceFlags |= - onlyUnreliable ? RelationComparisonResult.ReportsUnreliable : RelationComparisonResult.ReportsUnmeasurable; - return originalHandler!(onlyUnreliable); + variance === VarianceFlags.Awaited ? RelationComparisonResult.ReportsAwaited : + variance === VarianceFlags.Unreliable ? RelationComparisonResult.ReportsUnreliable : + RelationComparisonResult.ReportsUnmeasurable; + return originalHandler!(variance); }; } const result = expandingFlags !== ExpandingFlags.Both ? structuredTypeRelatedTo(source, target, reportErrors, intersectionState) : Ternary.Maybe; @@ -15966,7 +15971,7 @@ namespace ts { } else if (target.flags & TypeFlags.Awaited && source.flags & TypeFlags.Awaited) { const targetType = (target).awaitedType; - const sourceType = instantiateType((source).awaitedType, makeFunctionTypeMapper(reportUnreliableMarkers)); + const sourceType = instantiateType((source).awaitedType, makeFunctionTypeMapper(reportAwaitedMarkers)); // An `awaited S` is related to an `awaited T` if `S` is related to `T`: // // S <: T ⇒ awaited S <: awaited T @@ -16250,14 +16255,21 @@ namespace ts { function reportUnmeasurableMarkers(p: TypeParameter) { if (outofbandVarianceMarkerHandler && (p === markerSuperType || p === markerSubType || p === markerOtherType)) { - outofbandVarianceMarkerHandler(/*onlyUnreliable*/ false); + outofbandVarianceMarkerHandler(VarianceFlags.Unmeasurable); } return p; } function reportUnreliableMarkers(p: TypeParameter) { if (outofbandVarianceMarkerHandler && (p === markerSuperType || p === markerSubType || p === markerOtherType)) { - outofbandVarianceMarkerHandler(/*onlyUnreliable*/ true); + outofbandVarianceMarkerHandler(VarianceFlags.Unreliable); + } + return p; + } + + function reportAwaitedMarkers(p: TypeParameter) { + if (outofbandVarianceMarkerHandler && (p === markerSuperType || p === markerSubType || p === markerOtherType)) { + outofbandVarianceMarkerHandler(VarianceFlags.Awaited); } return p; } @@ -16971,8 +16983,12 @@ namespace ts { for (const tp of typeParameters) { let unmeasurable = false; let unreliable = false; + let awaited = false; const oldHandler = outofbandVarianceMarkerHandler; - outofbandVarianceMarkerHandler = (onlyUnreliable) => onlyUnreliable ? unreliable = true : unmeasurable = true; + outofbandVarianceMarkerHandler = (variance) => + variance === VarianceFlags.Awaited ? awaited = true : + variance === VarianceFlags.Unreliable ? unreliable = true : + unmeasurable = true; // We first compare instantiations where the type parameter is replaced with // marker types that have a known subtype relationship. From this we can infer // invariance, covariance, contravariance or bivariance. @@ -16994,6 +17010,9 @@ namespace ts { if (unreliable) { variance |= VarianceFlags.Unreliable; } + if (awaited) { + variance |= VarianceFlags.Awaited; + } variances.push(variance); } cache.variances = variances; @@ -18449,6 +18468,9 @@ namespace ts { if (i < variances.length && (variances[i] & VarianceFlags.VarianceMask) === VarianceFlags.Contravariant) { inferFromContravariantTypes(sourceTypes[i], targetTypes[i]); } + else if (variances[i] & VarianceFlags.Awaited) { + inferFromTypes(unwrapAwaitedType(sourceTypes[i]), targetTypes[i]); + } else { inferFromTypes(sourceTypes[i], targetTypes[i]); } diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 6a1f30f557fdf..a40034f1bec3a 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -1231,7 +1231,7 @@ namespace ts { enableCPUProfiler, disableCPUProfiler, realpath, - debugMode: some(process.execArgv, arg => /^--(inspect|debug)(-brk)?(=\d+)?$/i.test(arg)), + debugMode: !!process.env.NODE_INSPECTOR_IPC || some(process.execArgv, arg => /^--(inspect|debug)(-brk)?(=\d+)?$/i.test(arg)), tryEnableSourceMapsForHost() { try { require("source-map-support").install(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8d10ce0a6e581..8bb3cfe40e2a0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -641,7 +641,8 @@ namespace ts { ReportsUnmeasurable = 1 << 3, ReportsUnreliable = 1 << 4, - ReportsMask = ReportsUnmeasurable | ReportsUnreliable + ReportsAwaited = 1 << 5, + ReportsMask = ReportsUnmeasurable | ReportsUnreliable | ReportsAwaited } export interface Node extends TextRange { @@ -4608,7 +4609,8 @@ namespace ts { VarianceMask = Invariant | Covariant | Contravariant | Independent, // Mask containing all measured variances without the unmeasurable flag Unmeasurable = 1 << 3, // Variance result is unusable - relationship relies on structural comparisons which are not reflected in generic relationships Unreliable = 1 << 4, // Variance result is unreliable - checking may produce false negatives, but not false positives - AllowsStructuralFallback = Unmeasurable | Unreliable, + Awaited = 1 << 5, // type argument is awaited + AllowsStructuralFallback = Unmeasurable | Unreliable | Awaited, } // Generic class and interface types diff --git a/tests/baselines/reference/awaitedInference.js b/tests/baselines/reference/awaitedInference.js index 8c2fe01afb252..40d855636520b 100644 --- a/tests/baselines/reference/awaitedInference.js +++ b/tests/baselines/reference/awaitedInference.js @@ -13,7 +13,12 @@ function f() { } const x = f(); // number const y = f>(); // number ? -const z = f | number>(); // number ? +const z = f | number>(); // number ? + +// https://github.com/microsoft/TypeScript/issues/37526 +function f1(a: T): Promise { + return new Promise(r => r(a)); +} //// [awaitedInference.js] var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { @@ -62,3 +67,7 @@ function f() { var x = f(); // number var y = f(); // number ? var z = f(); // number ? +// https://github.com/microsoft/TypeScript/issues/37526 +function f1(a) { + return new Promise(function (r) { return r(a); }); +} diff --git a/tests/baselines/reference/awaitedInference.symbols b/tests/baselines/reference/awaitedInference.symbols index 82afccc34742f..6c0309883311e 100644 --- a/tests/baselines/reference/awaitedInference.symbols +++ b/tests/baselines/reference/awaitedInference.symbols @@ -26,12 +26,12 @@ type UnwrapAwaited = T extends awaited infer Inner ? Inner : T; type Result1 = UnwrapAwaited>; // number >Result1 : Symbol(Result1, Decl(awaitedInference.ts, 4, 66)) >UnwrapAwaited : Symbol(UnwrapAwaited, Decl(awaitedInference.ts, 2, 36)) ->Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) type Result2 = UnwrapAwaited | number>; // number >Result2 : Symbol(Result2, Decl(awaitedInference.ts, 5, 54)) >UnwrapAwaited : Symbol(UnwrapAwaited, Decl(awaitedInference.ts, 2, 36)) ->Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) function f() { >f : Symbol(f, Decl(awaitedInference.ts, 6, 63)) @@ -58,10 +58,25 @@ const x = f(); // number const y = f>(); // number ? >y : Symbol(y, Decl(awaitedInference.ts, 13, 5)) >f : Symbol(f, Decl(awaitedInference.ts, 6, 63)) ->Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) const z = f | number>(); // number ? >z : Symbol(z, Decl(awaitedInference.ts, 14, 5)) >f : Symbol(f, Decl(awaitedInference.ts, 6, 63)) ->Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +// https://github.com/microsoft/TypeScript/issues/37526 +function f1(a: T): Promise { +>f1 : Symbol(f1, Decl(awaitedInference.ts, 14, 40)) +>T : Symbol(T, Decl(awaitedInference.ts, 17, 12)) +>a : Symbol(a, Decl(awaitedInference.ts, 17, 15)) +>T : Symbol(T, Decl(awaitedInference.ts, 17, 12)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>T : Symbol(T, Decl(awaitedInference.ts, 17, 12)) + + return new Promise(r => r(a)); +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>r : Symbol(r, Decl(awaitedInference.ts, 18, 23)) +>r : Symbol(r, Decl(awaitedInference.ts, 18, 23)) +>a : Symbol(a, Decl(awaitedInference.ts, 17, 15)) +} diff --git a/tests/baselines/reference/awaitedInference.types b/tests/baselines/reference/awaitedInference.types index 91e7c43134e94..ab23d5a3dd0ed 100644 --- a/tests/baselines/reference/awaitedInference.types +++ b/tests/baselines/reference/awaitedInference.types @@ -53,3 +53,17 @@ const z = f | number>(); // number ? >f | number>() : [number | Promise, number] >f : () => [UnwrapAwaited, UnwrapAwaited] +// https://github.com/microsoft/TypeScript/issues/37526 +function f1(a: T): Promise { +>f1 : (a: T) => Promise +>a : T + + return new Promise(r => r(a)); +>new Promise(r => r(a)) : Promise +>Promise : PromiseConstructor +>r => r(a) : (r: (value?: T | awaited T | PromiseLike) => void) => void +>r : (value?: T | awaited T | PromiseLike) => void +>r(a) : void +>r : (value?: T | awaited T | PromiseLike) => void +>a : T +} diff --git a/tests/cases/conformance/types/awaited/awaitedInference.ts b/tests/cases/conformance/types/awaited/awaitedInference.ts index fcc88aee6d92c..44f2ee86207e2 100644 --- a/tests/cases/conformance/types/awaited/awaitedInference.ts +++ b/tests/cases/conformance/types/awaited/awaitedInference.ts @@ -1,3 +1,5 @@ +// @lib: es2015 + declare function foo(f: () => PromiseLike, x: T): void; declare const nullOrNumber: number | null; foo(async () => nullOrNumber, null); @@ -12,4 +14,9 @@ function f() { } const x = f(); // number const y = f>(); // number ? -const z = f | number>(); // number ? \ No newline at end of file +const z = f | number>(); // number ? + +// https://github.com/microsoft/TypeScript/issues/37526 +function f1(a: T): Promise { + return new Promise(r => r(a)); +} \ No newline at end of file From d37484ee774f6a7f688e792e3ded9a94d4a79fcb Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 24 Mar 2020 15:56:00 -0700 Subject: [PATCH 2/4] Perform structural inference for type parameters that are awaited --- src/compiler/checker.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 45f66a5e3cea4..711a57effd8d7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18218,10 +18218,10 @@ namespace ts { propagationType = savePropagationType; return; } - if (source.aliasSymbol && source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) { + if (source.aliasSymbol && source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol && + inferFromTypeArguments(source.aliasTypeArguments, target.aliasTypeArguments!, getAliasVariances(source.aliasSymbol))) { // Source and target are types originating in the same generic type alias declaration. // Simply infer from source type arguments to target type arguments. - inferFromTypeArguments(source.aliasTypeArguments, target.aliasTypeArguments!, getAliasVariances(source.aliasSymbol)); return; } if (source === target && source.flags & TypeFlags.UnionOrIntersection) { @@ -18341,9 +18341,9 @@ namespace ts { } if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && ( (source).target === (target).target || isArrayType(source) && isArrayType(target)) && - !((source).node && (target).node)) { + !((source).node && (target).node) && + inferFromTypeArguments(getTypeArguments(source), getTypeArguments(target), getVariances((source).target))) { // If source and target are references to the same generic type, infer from type arguments - inferFromTypeArguments(getTypeArguments(source), getTypeArguments(target), getVariances((source).target)); } else if (source.flags & TypeFlags.Index && target.flags & TypeFlags.Index) { contravariant = !contravariant; @@ -18463,6 +18463,9 @@ namespace ts { } function inferFromTypeArguments(sourceTypes: readonly Type[], targetTypes: readonly Type[], variances: readonly VarianceFlags[]) { + if (some(variances, variance => !!(variance & VarianceFlags.Awaited))) { + return false; + } const count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length; for (let i = 0; i < count; i++) { if (i < variances.length && (variances[i] & VarianceFlags.VarianceMask) === VarianceFlags.Contravariant) { @@ -18475,6 +18478,7 @@ namespace ts { inferFromTypes(sourceTypes[i], targetTypes[i]); } } + return true; } function inferFromContravariantTypes(source: Type, target: Type) { @@ -18680,9 +18684,9 @@ namespace ts { function inferFromObjectTypesWorker(source: Type, target: Type) { if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && ( - (source).target === (target).target || isArrayType(source) && isArrayType(target))) { + (source).target === (target).target || isArrayType(source) && isArrayType(target)) && + inferFromTypeArguments(getTypeArguments(source), getTypeArguments(target), getVariances((source).target))) { // If source and target are references to the same generic type, infer from type arguments - inferFromTypeArguments(getTypeArguments(source), getTypeArguments(target), getVariances((source).target)); return; } if (isGenericMappedType(source) && isGenericMappedType(target)) { From 70f92ff7751bad982f63cea0a3f8a8f9e32f1359 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 24 Mar 2020 15:56:21 -0700 Subject: [PATCH 3/4] Accept new baselines --- tests/baselines/reference/awaited.types | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/baselines/reference/awaited.types b/tests/baselines/reference/awaited.types index 790e209a80a0f..d08bb583baaf0 100644 --- a/tests/baselines/reference/awaited.types +++ b/tests/baselines/reference/awaited.types @@ -391,15 +391,15 @@ f5(makePromise(1)).then(x => x); f5(makePromise(makePromise(1))).then(x => x); >f5(makePromise(makePromise(1))).then(x => x) : Promise ->f5(makePromise(makePromise(1))).then : , TResult2 = never>(onfulfilled?: (value: number) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise ->f5(makePromise(makePromise(1))) : Promise> +>f5(makePromise(makePromise(1))).then : (onfulfilled?: (value: number) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>f5(makePromise(makePromise(1))) : Promise >f5 : (u: Promise) => Promise >makePromise(makePromise(1)) : Promise> >makePromise : (x: T) => Promise >makePromise(1) : Promise >makePromise : (x: T) => Promise >1 : 1 ->then : , TResult2 = never>(onfulfilled?: (value: number) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>then : (onfulfilled?: (value: number) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise >x => x : (x: number) => number >x : number >x : number From 360ac47b56e95d4b0fc40852b8849ee4ca9b3e41 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 24 Mar 2020 16:09:06 -0700 Subject: [PATCH 4/4] Remove dead code --- src/compiler/checker.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 711a57effd8d7..df0c6a983b1ba 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18471,9 +18471,6 @@ namespace ts { if (i < variances.length && (variances[i] & VarianceFlags.VarianceMask) === VarianceFlags.Contravariant) { inferFromContravariantTypes(sourceTypes[i], targetTypes[i]); } - else if (variances[i] & VarianceFlags.Awaited) { - inferFromTypes(unwrapAwaitedType(sourceTypes[i]), targetTypes[i]); - } else { inferFromTypes(sourceTypes[i], targetTypes[i]); }