From 9da2a9aa840746b6a3f5dc1fe90709cd75e677db Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 11 Jan 2023 15:28:53 -0800 Subject: [PATCH] Fix inferences between alias type arguments and defaulted alias type arguments (#51771) --- src/compiler/checker.ts | 14 +- ...rtedAliasedConditionalTypeInstantiation.js | 50 ++++++ ...liasedConditionalTypeInstantiation.symbols | 163 ++++++++++++++++++ ...dAliasedConditionalTypeInstantiation.types | 95 ++++++++++ ...rtedAliasedConditionalTypeInstantiation.ts | 45 +++++ 5 files changed, 364 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/importedAliasedConditionalTypeInstantiation.js create mode 100644 tests/baselines/reference/importedAliasedConditionalTypeInstantiation.symbols create mode 100644 tests/baselines/reference/importedAliasedConditionalTypeInstantiation.types create mode 100644 tests/cases/compiler/importedAliasedConditionalTypeInstantiation.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index db756ad74b838..234b9ef18c675 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20871,7 +20871,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (variances === emptyArray) { return Ternary.Unknown; } - const varianceResult = relateVariances(source.aliasTypeArguments, target.aliasTypeArguments, variances, intersectionState); + const params = getSymbolLinks(source.aliasSymbol).typeParameters!; + const minParams = getMinTypeArgumentCount(params); + const sourceTypes = fillMissingTypeArguments(source.aliasTypeArguments, params, minParams, isInJSFile(source.aliasSymbol.valueDeclaration)); + const targetTypes = fillMissingTypeArguments(target.aliasTypeArguments, params, minParams, isInJSFile(source.aliasSymbol.valueDeclaration)); + const varianceResult = relateVariances(sourceTypes, targetTypes, variances, intersectionState); if (varianceResult !== undefined) { return varianceResult; } @@ -23805,8 +23809,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (source.aliasSymbol && source.aliasSymbol === target.aliasSymbol) { if (source.aliasTypeArguments) { // 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)); + // Simply infer from source type arguments to target type arguments, with defaults applied. + const params = getSymbolLinks(source.aliasSymbol).typeParameters!; + const minParams = getMinTypeArgumentCount(params); + const sourceTypes = fillMissingTypeArguments(source.aliasTypeArguments, params, minParams, isInJSFile(source.aliasSymbol.valueDeclaration)); + const targetTypes = fillMissingTypeArguments(target.aliasTypeArguments, params, minParams, isInJSFile(source.aliasSymbol.valueDeclaration)); + inferFromTypeArguments(sourceTypes, targetTypes!, getAliasVariances(source.aliasSymbol)); } // And if there weren't any type arguments, there's no reason to run inference as the types must be the same. return; diff --git a/tests/baselines/reference/importedAliasedConditionalTypeInstantiation.js b/tests/baselines/reference/importedAliasedConditionalTypeInstantiation.js new file mode 100644 index 0000000000000..6cdcc54d98189 --- /dev/null +++ b/tests/baselines/reference/importedAliasedConditionalTypeInstantiation.js @@ -0,0 +1,50 @@ +//// [tests/cases/compiler/importedAliasedConditionalTypeInstantiation.ts] //// + +//// [index.d.ts] +export type Handler = ( + event: TEvent, + context: {}, + callback: Callback, +) => void | Promise; + +export type Callback = (error?: Error | string | null, result?: TResult) => void; + +//// [index.d.ts] +import { Handler, Callback } from 'aws-lambda'; +declare namespace lambdaTester { + type HandlerEvent = T extends Handler ? TEvent : never; + type HandlerResult = T extends Handler ? TResult : never; + type HandlerError = T extends Handler + ? NonNullable>['0']> + : never; + + interface VerifierFn { + (result: S, additional?: any): void | Promise; + (result: S, additional?: any, done?: () => {}): void; + } + type Verifier = S extends HandlerError + ? S extends string + ? VerifierFn + : S extends Error + ? VerifierFn + : never + : VerifierFn; + + class LambdaTester { + event(event: HandlerEvent): this; + } +} + +declare function lambdaTester(handler: T): lambdaTester.LambdaTester; + +export = lambdaTester; +//// [index.ts] +import * as lambdaTester from 'lambda-tester'; +import { Handler } from 'aws-lambda'; + +type Actual = lambdaTester.Verifier>; +type Expected = lambdaTester.Verifier>>; + +//// [index.js] +"use strict"; +exports.__esModule = true; diff --git a/tests/baselines/reference/importedAliasedConditionalTypeInstantiation.symbols b/tests/baselines/reference/importedAliasedConditionalTypeInstantiation.symbols new file mode 100644 index 0000000000000..8e2b87d235d40 --- /dev/null +++ b/tests/baselines/reference/importedAliasedConditionalTypeInstantiation.symbols @@ -0,0 +1,163 @@ +=== tests/cases/compiler/node_modules/aws-lambda/index.d.ts === +export type Handler = ( +>Handler : Symbol(Handler, Decl(index.d.ts, 0, 0)) +>TEvent : Symbol(TEvent, Decl(index.d.ts, 0, 20)) +>TResult : Symbol(TResult, Decl(index.d.ts, 0, 33)) + + event: TEvent, +>event : Symbol(event, Decl(index.d.ts, 0, 52)) +>TEvent : Symbol(TEvent, Decl(index.d.ts, 0, 20)) + + context: {}, +>context : Symbol(context, Decl(index.d.ts, 1, 18)) + + callback: Callback, +>callback : Symbol(callback, Decl(index.d.ts, 2, 16)) +>Callback : Symbol(Callback, Decl(index.d.ts, 4, 29)) +>TResult : Symbol(TResult, Decl(index.d.ts, 0, 33)) + +) => void | Promise; +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>TResult : Symbol(TResult, Decl(index.d.ts, 0, 33)) + +export type Callback = (error?: Error | string | null, result?: TResult) => void; +>Callback : Symbol(Callback, Decl(index.d.ts, 4, 29)) +>TResult : Symbol(TResult, Decl(index.d.ts, 6, 21)) +>error : Symbol(error, Decl(index.d.ts, 6, 39)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>result : Symbol(result, Decl(index.d.ts, 6, 69)) +>TResult : Symbol(TResult, Decl(index.d.ts, 6, 21)) + +=== tests/cases/compiler/node_modules/lambda-tester/index.d.ts === +import { Handler, Callback } from 'aws-lambda'; +>Handler : Symbol(Handler, Decl(index.d.ts, 0, 8)) +>Callback : Symbol(Callback, Decl(index.d.ts, 0, 17)) + +declare namespace lambdaTester { +>lambdaTester : Symbol(lambdaTester, Decl(index.d.ts, 23, 1), Decl(index.d.ts, 0, 47)) + + type HandlerEvent = T extends Handler ? TEvent : never; +>HandlerEvent : Symbol(HandlerEvent, Decl(index.d.ts, 1, 32)) +>T : Symbol(T, Decl(index.d.ts, 2, 22)) +>Handler : Symbol(Handler, Decl(index.d.ts, 0, 8)) +>T : Symbol(T, Decl(index.d.ts, 2, 22)) +>Handler : Symbol(Handler, Decl(index.d.ts, 0, 8)) +>TEvent : Symbol(TEvent, Decl(index.d.ts, 2, 66)) +>TEvent : Symbol(TEvent, Decl(index.d.ts, 2, 66)) + + type HandlerResult = T extends Handler ? TResult : never; +>HandlerResult : Symbol(HandlerResult, Decl(index.d.ts, 2, 92)) +>T : Symbol(T, Decl(index.d.ts, 3, 23)) +>Handler : Symbol(Handler, Decl(index.d.ts, 0, 8)) +>T : Symbol(T, Decl(index.d.ts, 3, 23)) +>Handler : Symbol(Handler, Decl(index.d.ts, 0, 8)) +>TResult : Symbol(TResult, Decl(index.d.ts, 3, 72)) +>TResult : Symbol(TResult, Decl(index.d.ts, 3, 72)) + + type HandlerError = T extends Handler +>HandlerError : Symbol(HandlerError, Decl(index.d.ts, 3, 100)) +>T : Symbol(T, Decl(index.d.ts, 4, 22)) +>Handler : Symbol(Handler, Decl(index.d.ts, 0, 8)) +>T : Symbol(T, Decl(index.d.ts, 4, 22)) +>Handler : Symbol(Handler, Decl(index.d.ts, 0, 8)) +>TResult : Symbol(TResult, Decl(index.d.ts, 4, 71)) + + ? NonNullable>['0']> +>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) +>Parameters : Symbol(Parameters, Decl(lib.es5.d.ts, --, --)) +>Callback : Symbol(Callback, Decl(index.d.ts, 0, 17)) +>TResult : Symbol(TResult, Decl(index.d.ts, 4, 71)) + + : never; + + interface VerifierFn { +>VerifierFn : Symbol(VerifierFn, Decl(index.d.ts, 6, 16)) +>S : Symbol(S, Decl(index.d.ts, 8, 25)) + + (result: S, additional?: any): void | Promise; +>result : Symbol(result, Decl(index.d.ts, 9, 9)) +>S : Symbol(S, Decl(index.d.ts, 8, 25)) +>additional : Symbol(additional, Decl(index.d.ts, 9, 19)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) + + (result: S, additional?: any, done?: () => {}): void; +>result : Symbol(result, Decl(index.d.ts, 10, 9)) +>S : Symbol(S, Decl(index.d.ts, 8, 25)) +>additional : Symbol(additional, Decl(index.d.ts, 10, 19)) +>done : Symbol(done, Decl(index.d.ts, 10, 37)) + } + type Verifier = S extends HandlerError +>Verifier : Symbol(Verifier, Decl(index.d.ts, 11, 5)) +>S : Symbol(S, Decl(index.d.ts, 12, 18)) +>S : Symbol(S, Decl(index.d.ts, 12, 18)) +>HandlerError : Symbol(HandlerError, Decl(index.d.ts, 3, 100)) +>Handler : Symbol(Handler, Decl(index.d.ts, 0, 8)) + + ? S extends string +>S : Symbol(S, Decl(index.d.ts, 12, 18)) + + ? VerifierFn +>VerifierFn : Symbol(VerifierFn, Decl(index.d.ts, 6, 16)) + + : S extends Error +>S : Symbol(S, Decl(index.d.ts, 12, 18)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + ? VerifierFn +>VerifierFn : Symbol(VerifierFn, Decl(index.d.ts, 6, 16)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + : never + : VerifierFn; +>VerifierFn : Symbol(VerifierFn, Decl(index.d.ts, 6, 16)) +>S : Symbol(S, Decl(index.d.ts, 12, 18)) + + class LambdaTester { +>LambdaTester : Symbol(LambdaTester, Decl(index.d.ts, 18, 24)) +>T : Symbol(T, Decl(index.d.ts, 20, 23)) +>Handler : Symbol(Handler, Decl(index.d.ts, 0, 8)) + + event(event: HandlerEvent): this; +>event : Symbol(LambdaTester.event, Decl(index.d.ts, 20, 43)) +>event : Symbol(event, Decl(index.d.ts, 21, 14)) +>HandlerEvent : Symbol(HandlerEvent, Decl(index.d.ts, 1, 32)) +>T : Symbol(T, Decl(index.d.ts, 20, 23)) + } +} + +declare function lambdaTester(handler: T): lambdaTester.LambdaTester; +>lambdaTester : Symbol(lambdaTester, Decl(index.d.ts, 23, 1), Decl(index.d.ts, 0, 47)) +>T : Symbol(T, Decl(index.d.ts, 25, 30)) +>Handler : Symbol(Handler, Decl(index.d.ts, 0, 8)) +>handler : Symbol(handler, Decl(index.d.ts, 25, 49)) +>T : Symbol(T, Decl(index.d.ts, 25, 30)) +>lambdaTester : Symbol(lambdaTester, Decl(index.d.ts, 23, 1), Decl(index.d.ts, 0, 47)) +>LambdaTester : Symbol(lambdaTester.LambdaTester, Decl(index.d.ts, 18, 24)) +>T : Symbol(T, Decl(index.d.ts, 25, 30)) + +export = lambdaTester; +>lambdaTester : Symbol(lambdaTester, Decl(index.d.ts, 23, 1), Decl(index.d.ts, 0, 47)) + +=== tests/cases/compiler/index.ts === +import * as lambdaTester from 'lambda-tester'; +>lambdaTester : Symbol(lambdaTester, Decl(index.ts, 0, 6)) + +import { Handler } from 'aws-lambda'; +>Handler : Symbol(Handler, Decl(index.ts, 1, 8)) + +type Actual = lambdaTester.Verifier>; +>Actual : Symbol(Actual, Decl(index.ts, 1, 37)) +>lambdaTester : Symbol(lambdaTester, Decl(index.ts, 0, 6)) +>Verifier : Symbol(lambdaTester.Verifier, Decl(index.d.ts, 11, 5)) +>lambdaTester : Symbol(lambdaTester, Decl(index.ts, 0, 6)) +>HandlerResult : Symbol(lambdaTester.HandlerResult, Decl(index.d.ts, 2, 92)) +>Handler : Symbol(Handler, Decl(index.ts, 1, 8)) + +type Expected = lambdaTester.Verifier>>; +>Expected : Symbol(Expected, Decl(index.ts, 3, 73)) +>lambdaTester : Symbol(lambdaTester, Decl(index.ts, 0, 6)) +>Verifier : Symbol(lambdaTester.Verifier, Decl(index.d.ts, 11, 5)) +>lambdaTester : Symbol(lambdaTester, Decl(index.ts, 0, 6)) +>HandlerResult : Symbol(lambdaTester.HandlerResult, Decl(index.d.ts, 2, 92)) +>Handler : Symbol(Handler, Decl(index.ts, 1, 8)) + diff --git a/tests/baselines/reference/importedAliasedConditionalTypeInstantiation.types b/tests/baselines/reference/importedAliasedConditionalTypeInstantiation.types new file mode 100644 index 0000000000000..968fc10d0b597 --- /dev/null +++ b/tests/baselines/reference/importedAliasedConditionalTypeInstantiation.types @@ -0,0 +1,95 @@ +=== tests/cases/compiler/node_modules/aws-lambda/index.d.ts === +export type Handler = ( +>Handler : Handler + + event: TEvent, +>event : TEvent + + context: {}, +>context : {} + + callback: Callback, +>callback : Callback + +) => void | Promise; + +export type Callback = (error?: Error | string | null, result?: TResult) => void; +>Callback : Callback +>error : string | Error +>null : null +>result : TResult + +=== tests/cases/compiler/node_modules/lambda-tester/index.d.ts === +import { Handler, Callback } from 'aws-lambda'; +>Handler : any +>Callback : any + +declare namespace lambdaTester { +>lambdaTester : typeof lambdaTester + + type HandlerEvent = T extends Handler ? TEvent : never; +>HandlerEvent : HandlerEvent + + type HandlerResult = T extends Handler ? TResult : never; +>HandlerResult : HandlerResult + + type HandlerError = T extends Handler +>HandlerError : HandlerError + + ? NonNullable>['0']> + : never; + + interface VerifierFn { + (result: S, additional?: any): void | Promise; +>result : S +>additional : any + + (result: S, additional?: any, done?: () => {}): void; +>result : S +>additional : any +>done : () => {} + } + type Verifier = S extends HandlerError +>Verifier : Verifier + + ? S extends string + ? VerifierFn + : S extends Error + ? VerifierFn + : never + : VerifierFn; + + class LambdaTester { +>LambdaTester : LambdaTester + + event(event: HandlerEvent): this; +>event : (event: HandlerEvent) => this +>event : HandlerEvent + } +} + +declare function lambdaTester(handler: T): lambdaTester.LambdaTester; +>lambdaTester : typeof lambdaTester +>handler : T +>lambdaTester : any + +export = lambdaTester; +>lambdaTester : typeof lambdaTester + +=== tests/cases/compiler/index.ts === +import * as lambdaTester from 'lambda-tester'; +>lambdaTester : typeof lambdaTester + +import { Handler } from 'aws-lambda'; +>Handler : any + +type Actual = lambdaTester.Verifier>; +>Actual : lambdaTester.VerifierFn | lambdaTester.VerifierFn | lambdaTester.VerifierFn +>lambdaTester : any +>lambdaTester : any + +type Expected = lambdaTester.Verifier>>; +>Expected : lambdaTester.VerifierFn | lambdaTester.VerifierFn | lambdaTester.VerifierFn +>lambdaTester : any +>lambdaTester : any + diff --git a/tests/cases/compiler/importedAliasedConditionalTypeInstantiation.ts b/tests/cases/compiler/importedAliasedConditionalTypeInstantiation.ts new file mode 100644 index 0000000000000..96db52728c46f --- /dev/null +++ b/tests/cases/compiler/importedAliasedConditionalTypeInstantiation.ts @@ -0,0 +1,45 @@ +// @filename: node_modules/aws-lambda/index.d.ts +export type Handler = ( + event: TEvent, + context: {}, + callback: Callback, +) => void | Promise; + +export type Callback = (error?: Error | string | null, result?: TResult) => void; + +// @filename: node_modules/lambda-tester/index.d.ts +import { Handler, Callback } from 'aws-lambda'; +declare namespace lambdaTester { + type HandlerEvent = T extends Handler ? TEvent : never; + type HandlerResult = T extends Handler ? TResult : never; + type HandlerError = T extends Handler + ? NonNullable>['0']> + : never; + + interface VerifierFn { + (result: S, additional?: any): void | Promise; + (result: S, additional?: any, done?: () => {}): void; + } + type Verifier = S extends HandlerError + ? S extends string + ? VerifierFn + : S extends Error + ? VerifierFn + : never + : VerifierFn; + + class LambdaTester { + event(event: HandlerEvent): this; + } +} + +declare function lambdaTester(handler: T): lambdaTester.LambdaTester; + +export = lambdaTester; +// @filename: index.ts + +import * as lambdaTester from 'lambda-tester'; +import { Handler } from 'aws-lambda'; + +type Actual = lambdaTester.Verifier>; +type Expected = lambdaTester.Verifier>>; \ No newline at end of file