diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4aee170f195d4..474861589d2b1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10305,16 +10305,32 @@ namespace ts { // If the given type is an object or union type, if that type has a single signature, and if // that signature is non-generic, return the signature. Otherwise return undefined. - function getNonGenericSignature(type: Type): Signature { + function getNonGenericSignature(type: Type, node: FunctionExpression | ArrowFunction | MethodDeclaration): Signature { const signatures = getSignaturesOfStructuredType(type, SignatureKind.Call); if (signatures.length === 1) { const signature = signatures[0]; - if (!signature.typeParameters) { + if (!signature.typeParameters && !isAritySmaller(signature, node)) { return signature; } } } + /** If the contextual signature has fewer parameters than the function expression, do not use it */ + function isAritySmaller(signature: Signature, target: FunctionExpression | ArrowFunction | MethodDeclaration) { + let targetParameterCount = 0; + for (; targetParameterCount < target.parameters.length; targetParameterCount++) { + const param = target.parameters[targetParameterCount]; + if (param.initializer || param.questionToken || param.dotDotDotToken || isJSDocOptionalParameter(param)) { + break; + } + } + if (target.parameters.length && parameterIsThisKeyword(target.parameters[0])) { + targetParameterCount--; + } + const sourceLength = signature.hasRestParameter ? Number.MAX_VALUE : signature.parameters.length; + return sourceLength < targetParameterCount; + } + function isFunctionExpressionOrArrowFunction(node: Node): node is FunctionExpression | ArrowFunction { return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction; } @@ -10344,12 +10360,12 @@ namespace ts { return undefined; } if (!(type.flags & TypeFlags.Union)) { - return getNonGenericSignature(type); + return getNonGenericSignature(type, node); } let signatureList: Signature[]; const types = (type).types; for (const current of types) { - const signature = getNonGenericSignature(current); + const signature = getNonGenericSignature(current, node); if (signature) { if (!signatureList) { // This signature will contribute to contextual union signature diff --git a/tests/baselines/reference/contextualTypingOfTooShortOverloads.js b/tests/baselines/reference/contextualTypingOfTooShortOverloads.js new file mode 100644 index 0000000000000..55f1361f5a857 --- /dev/null +++ b/tests/baselines/reference/contextualTypingOfTooShortOverloads.js @@ -0,0 +1,59 @@ +//// [contextualTypingOfTooShortOverloads.ts] +// small repro from #11875 +var use: Overload; +use((req, res) => {}); + +interface Overload { + (handler1: (req1: string) => void): void; + (handler2: (req2: number, res2: number) => void): void; +} +// larger repro from #11875 +let app: MyApp; +app.use((err: any, req, res, next) => { return; }); + + +interface MyApp { + use: IRouterHandler & IRouterMatcher; +} + +interface IRouterHandler { + (...handlers: RequestHandler[]): T; + (...handlers: RequestHandlerParams[]): T; +} + +interface IRouterMatcher { + (path: PathParams, ...handlers: RequestHandler[]): T; + (path: PathParams, ...handlers: RequestHandlerParams[]): T; +} + +type PathParams = string | RegExp | (string | RegExp)[]; +type RequestHandlerParams = RequestHandler | ErrorRequestHandler | (RequestHandler | ErrorRequestHandler)[]; + +interface RequestHandler { + (req: Request, res: Response, next: NextFunction): any; +} + +interface ErrorRequestHandler { + (err: any, req: Request, res: Response, next: NextFunction): any; +} + +interface Request { + method: string; +} + +interface Response { + statusCode: number; +} + +interface NextFunction { + (err?: any): void; +} + + +//// [contextualTypingOfTooShortOverloads.js] +// small repro from #11875 +var use; +use(function (req, res) { }); +// larger repro from #11875 +var app; +app.use(function (err, req, res, next) { return; }); diff --git a/tests/baselines/reference/contextualTypingOfTooShortOverloads.symbols b/tests/baselines/reference/contextualTypingOfTooShortOverloads.symbols new file mode 100644 index 0000000000000..8f196cf48252d --- /dev/null +++ b/tests/baselines/reference/contextualTypingOfTooShortOverloads.symbols @@ -0,0 +1,139 @@ +=== tests/cases/compiler/contextualTypingOfTooShortOverloads.ts === +// small repro from #11875 +var use: Overload; +>use : Symbol(use, Decl(contextualTypingOfTooShortOverloads.ts, 1, 3)) +>Overload : Symbol(Overload, Decl(contextualTypingOfTooShortOverloads.ts, 2, 22)) + +use((req, res) => {}); +>use : Symbol(use, Decl(contextualTypingOfTooShortOverloads.ts, 1, 3)) +>req : Symbol(req, Decl(contextualTypingOfTooShortOverloads.ts, 2, 5)) +>res : Symbol(res, Decl(contextualTypingOfTooShortOverloads.ts, 2, 9)) + +interface Overload { +>Overload : Symbol(Overload, Decl(contextualTypingOfTooShortOverloads.ts, 2, 22)) + + (handler1: (req1: string) => void): void; +>handler1 : Symbol(handler1, Decl(contextualTypingOfTooShortOverloads.ts, 5, 5)) +>req1 : Symbol(req1, Decl(contextualTypingOfTooShortOverloads.ts, 5, 16)) + + (handler2: (req2: number, res2: number) => void): void; +>handler2 : Symbol(handler2, Decl(contextualTypingOfTooShortOverloads.ts, 6, 5)) +>req2 : Symbol(req2, Decl(contextualTypingOfTooShortOverloads.ts, 6, 16)) +>res2 : Symbol(res2, Decl(contextualTypingOfTooShortOverloads.ts, 6, 29)) +} +// larger repro from #11875 +let app: MyApp; +>app : Symbol(app, Decl(contextualTypingOfTooShortOverloads.ts, 9, 3)) +>MyApp : Symbol(MyApp, Decl(contextualTypingOfTooShortOverloads.ts, 10, 51)) + +app.use((err: any, req, res, next) => { return; }); +>app.use : Symbol(MyApp.use, Decl(contextualTypingOfTooShortOverloads.ts, 13, 17)) +>app : Symbol(app, Decl(contextualTypingOfTooShortOverloads.ts, 9, 3)) +>use : Symbol(MyApp.use, Decl(contextualTypingOfTooShortOverloads.ts, 13, 17)) +>err : Symbol(err, Decl(contextualTypingOfTooShortOverloads.ts, 10, 9)) +>req : Symbol(req, Decl(contextualTypingOfTooShortOverloads.ts, 10, 18)) +>res : Symbol(res, Decl(contextualTypingOfTooShortOverloads.ts, 10, 23)) +>next : Symbol(next, Decl(contextualTypingOfTooShortOverloads.ts, 10, 28)) + + +interface MyApp { +>MyApp : Symbol(MyApp, Decl(contextualTypingOfTooShortOverloads.ts, 10, 51)) + + use: IRouterHandler & IRouterMatcher; +>use : Symbol(MyApp.use, Decl(contextualTypingOfTooShortOverloads.ts, 13, 17)) +>IRouterHandler : Symbol(IRouterHandler, Decl(contextualTypingOfTooShortOverloads.ts, 15, 1)) +>IRouterMatcher : Symbol(IRouterMatcher, Decl(contextualTypingOfTooShortOverloads.ts, 20, 1)) +} + +interface IRouterHandler { +>IRouterHandler : Symbol(IRouterHandler, Decl(contextualTypingOfTooShortOverloads.ts, 15, 1)) +>T : Symbol(T, Decl(contextualTypingOfTooShortOverloads.ts, 17, 25)) + + (...handlers: RequestHandler[]): T; +>handlers : Symbol(handlers, Decl(contextualTypingOfTooShortOverloads.ts, 18, 5)) +>RequestHandler : Symbol(RequestHandler, Decl(contextualTypingOfTooShortOverloads.ts, 28, 108)) +>T : Symbol(T, Decl(contextualTypingOfTooShortOverloads.ts, 17, 25)) + + (...handlers: RequestHandlerParams[]): T; +>handlers : Symbol(handlers, Decl(contextualTypingOfTooShortOverloads.ts, 19, 5)) +>RequestHandlerParams : Symbol(RequestHandlerParams, Decl(contextualTypingOfTooShortOverloads.ts, 27, 56)) +>T : Symbol(T, Decl(contextualTypingOfTooShortOverloads.ts, 17, 25)) +} + +interface IRouterMatcher { +>IRouterMatcher : Symbol(IRouterMatcher, Decl(contextualTypingOfTooShortOverloads.ts, 20, 1)) +>T : Symbol(T, Decl(contextualTypingOfTooShortOverloads.ts, 22, 25)) + + (path: PathParams, ...handlers: RequestHandler[]): T; +>path : Symbol(path, Decl(contextualTypingOfTooShortOverloads.ts, 23, 5)) +>PathParams : Symbol(PathParams, Decl(contextualTypingOfTooShortOverloads.ts, 25, 1)) +>handlers : Symbol(handlers, Decl(contextualTypingOfTooShortOverloads.ts, 23, 22)) +>RequestHandler : Symbol(RequestHandler, Decl(contextualTypingOfTooShortOverloads.ts, 28, 108)) +>T : Symbol(T, Decl(contextualTypingOfTooShortOverloads.ts, 22, 25)) + + (path: PathParams, ...handlers: RequestHandlerParams[]): T; +>path : Symbol(path, Decl(contextualTypingOfTooShortOverloads.ts, 24, 5)) +>PathParams : Symbol(PathParams, Decl(contextualTypingOfTooShortOverloads.ts, 25, 1)) +>handlers : Symbol(handlers, Decl(contextualTypingOfTooShortOverloads.ts, 24, 22)) +>RequestHandlerParams : Symbol(RequestHandlerParams, Decl(contextualTypingOfTooShortOverloads.ts, 27, 56)) +>T : Symbol(T, Decl(contextualTypingOfTooShortOverloads.ts, 22, 25)) +} + +type PathParams = string | RegExp | (string | RegExp)[]; +>PathParams : Symbol(PathParams, Decl(contextualTypingOfTooShortOverloads.ts, 25, 1)) +>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +type RequestHandlerParams = RequestHandler | ErrorRequestHandler | (RequestHandler | ErrorRequestHandler)[]; +>RequestHandlerParams : Symbol(RequestHandlerParams, Decl(contextualTypingOfTooShortOverloads.ts, 27, 56)) +>RequestHandler : Symbol(RequestHandler, Decl(contextualTypingOfTooShortOverloads.ts, 28, 108)) +>ErrorRequestHandler : Symbol(ErrorRequestHandler, Decl(contextualTypingOfTooShortOverloads.ts, 32, 1)) +>RequestHandler : Symbol(RequestHandler, Decl(contextualTypingOfTooShortOverloads.ts, 28, 108)) +>ErrorRequestHandler : Symbol(ErrorRequestHandler, Decl(contextualTypingOfTooShortOverloads.ts, 32, 1)) + +interface RequestHandler { +>RequestHandler : Symbol(RequestHandler, Decl(contextualTypingOfTooShortOverloads.ts, 28, 108)) + + (req: Request, res: Response, next: NextFunction): any; +>req : Symbol(req, Decl(contextualTypingOfTooShortOverloads.ts, 31, 5)) +>Request : Symbol(Request, Decl(contextualTypingOfTooShortOverloads.ts, 36, 1)) +>res : Symbol(res, Decl(contextualTypingOfTooShortOverloads.ts, 31, 18)) +>Response : Symbol(Response, Decl(contextualTypingOfTooShortOverloads.ts, 40, 1)) +>next : Symbol(next, Decl(contextualTypingOfTooShortOverloads.ts, 31, 33)) +>NextFunction : Symbol(NextFunction, Decl(contextualTypingOfTooShortOverloads.ts, 44, 1)) +} + +interface ErrorRequestHandler { +>ErrorRequestHandler : Symbol(ErrorRequestHandler, Decl(contextualTypingOfTooShortOverloads.ts, 32, 1)) + + (err: any, req: Request, res: Response, next: NextFunction): any; +>err : Symbol(err, Decl(contextualTypingOfTooShortOverloads.ts, 35, 5)) +>req : Symbol(req, Decl(contextualTypingOfTooShortOverloads.ts, 35, 14)) +>Request : Symbol(Request, Decl(contextualTypingOfTooShortOverloads.ts, 36, 1)) +>res : Symbol(res, Decl(contextualTypingOfTooShortOverloads.ts, 35, 28)) +>Response : Symbol(Response, Decl(contextualTypingOfTooShortOverloads.ts, 40, 1)) +>next : Symbol(next, Decl(contextualTypingOfTooShortOverloads.ts, 35, 43)) +>NextFunction : Symbol(NextFunction, Decl(contextualTypingOfTooShortOverloads.ts, 44, 1)) +} + +interface Request { +>Request : Symbol(Request, Decl(contextualTypingOfTooShortOverloads.ts, 36, 1)) + + method: string; +>method : Symbol(Request.method, Decl(contextualTypingOfTooShortOverloads.ts, 38, 19)) +} + +interface Response { +>Response : Symbol(Response, Decl(contextualTypingOfTooShortOverloads.ts, 40, 1)) + + statusCode: number; +>statusCode : Symbol(Response.statusCode, Decl(contextualTypingOfTooShortOverloads.ts, 42, 20)) +} + +interface NextFunction { +>NextFunction : Symbol(NextFunction, Decl(contextualTypingOfTooShortOverloads.ts, 44, 1)) + + (err?: any): void; +>err : Symbol(err, Decl(contextualTypingOfTooShortOverloads.ts, 47, 5)) +} + diff --git a/tests/baselines/reference/contextualTypingOfTooShortOverloads.types b/tests/baselines/reference/contextualTypingOfTooShortOverloads.types new file mode 100644 index 0000000000000..769bd55d4b57b --- /dev/null +++ b/tests/baselines/reference/contextualTypingOfTooShortOverloads.types @@ -0,0 +1,143 @@ +=== tests/cases/compiler/contextualTypingOfTooShortOverloads.ts === +// small repro from #11875 +var use: Overload; +>use : Overload +>Overload : Overload + +use((req, res) => {}); +>use((req, res) => {}) : void +>use : Overload +>(req, res) => {} : (req: any, res: any) => void +>req : any +>res : any + +interface Overload { +>Overload : Overload + + (handler1: (req1: string) => void): void; +>handler1 : (req1: string) => void +>req1 : string + + (handler2: (req2: number, res2: number) => void): void; +>handler2 : (req2: number, res2: number) => void +>req2 : number +>res2 : number +} +// larger repro from #11875 +let app: MyApp; +>app : MyApp +>MyApp : MyApp + +app.use((err: any, req, res, next) => { return; }); +>app.use((err: any, req, res, next) => { return; }) : MyApp +>app.use : IRouterHandler & IRouterMatcher +>app : MyApp +>use : IRouterHandler & IRouterMatcher +>(err: any, req, res, next) => { return; } : (err: any, req: any, res: any, next: any) => void +>err : any +>req : any +>res : any +>next : any + + +interface MyApp { +>MyApp : MyApp + + use: IRouterHandler & IRouterMatcher; +>use : IRouterHandler & IRouterMatcher +>IRouterHandler : IRouterHandler +>IRouterMatcher : IRouterMatcher +} + +interface IRouterHandler { +>IRouterHandler : IRouterHandler +>T : T + + (...handlers: RequestHandler[]): T; +>handlers : RequestHandler[] +>RequestHandler : RequestHandler +>T : T + + (...handlers: RequestHandlerParams[]): T; +>handlers : RequestHandlerParams[] +>RequestHandlerParams : RequestHandlerParams +>T : T +} + +interface IRouterMatcher { +>IRouterMatcher : IRouterMatcher +>T : T + + (path: PathParams, ...handlers: RequestHandler[]): T; +>path : PathParams +>PathParams : PathParams +>handlers : RequestHandler[] +>RequestHandler : RequestHandler +>T : T + + (path: PathParams, ...handlers: RequestHandlerParams[]): T; +>path : PathParams +>PathParams : PathParams +>handlers : RequestHandlerParams[] +>RequestHandlerParams : RequestHandlerParams +>T : T +} + +type PathParams = string | RegExp | (string | RegExp)[]; +>PathParams : PathParams +>RegExp : RegExp +>RegExp : RegExp + +type RequestHandlerParams = RequestHandler | ErrorRequestHandler | (RequestHandler | ErrorRequestHandler)[]; +>RequestHandlerParams : RequestHandlerParams +>RequestHandler : RequestHandler +>ErrorRequestHandler : ErrorRequestHandler +>RequestHandler : RequestHandler +>ErrorRequestHandler : ErrorRequestHandler + +interface RequestHandler { +>RequestHandler : RequestHandler + + (req: Request, res: Response, next: NextFunction): any; +>req : Request +>Request : Request +>res : Response +>Response : Response +>next : NextFunction +>NextFunction : NextFunction +} + +interface ErrorRequestHandler { +>ErrorRequestHandler : ErrorRequestHandler + + (err: any, req: Request, res: Response, next: NextFunction): any; +>err : any +>req : Request +>Request : Request +>res : Response +>Response : Response +>next : NextFunction +>NextFunction : NextFunction +} + +interface Request { +>Request : Request + + method: string; +>method : string +} + +interface Response { +>Response : Response + + statusCode: number; +>statusCode : number +} + +interface NextFunction { +>NextFunction : NextFunction + + (err?: any): void; +>err : any +} + diff --git a/tests/baselines/reference/partiallyAnnotatedFunctionInferenceError.errors.txt b/tests/baselines/reference/partiallyAnnotatedFunctionInferenceError.errors.txt index a9fc9b68cf899..a6a4524592993 100644 --- a/tests/baselines/reference/partiallyAnnotatedFunctionInferenceError.errors.txt +++ b/tests/baselines/reference/partiallyAnnotatedFunctionInferenceError.errors.txt @@ -1,6 +1,6 @@ -tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(12,11): error TS2345: Argument of type '(t1: D, t2: D, t3: any) => void' is not assignable to parameter of type '(t: D, t1: D) => void'. -tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(13,11): error TS2345: Argument of type '(t1: D, t2: D, t3: any) => void' is not assignable to parameter of type '(t: D, t1: D) => void'. -tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(14,11): error TS2345: Argument of type '(t1: C, t2: C, t3: D) => void' is not assignable to parameter of type '(t: C, t1: C) => void'. +tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(12,11): error TS2345: Argument of type '(t1: D, t2: any, t3: any) => void' is not assignable to parameter of type '(t: any, t1: any) => void'. +tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(13,11): error TS2345: Argument of type '(t1: any, t2: D, t3: any) => void' is not assignable to parameter of type '(t: any, t1: any) => void'. +tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(14,11): error TS2345: Argument of type '(t1: any, t2: any, t3: D) => void' is not assignable to parameter of type '(t: any, t1: any) => void'. ==== tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts (3 errors) ==== @@ -17,11 +17,11 @@ tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partial // more args testError((t1: D, t2, t3) => {}) ~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type '(t1: D, t2: D, t3: any) => void' is not assignable to parameter of type '(t: D, t1: D) => void'. +!!! error TS2345: Argument of type '(t1: D, t2: any, t3: any) => void' is not assignable to parameter of type '(t: any, t1: any) => void'. testError((t1, t2: D, t3) => {}) ~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type '(t1: D, t2: D, t3: any) => void' is not assignable to parameter of type '(t: D, t1: D) => void'. +!!! error TS2345: Argument of type '(t1: any, t2: D, t3: any) => void' is not assignable to parameter of type '(t: any, t1: any) => void'. testError((t1, t2, t3: D) => {}) ~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type '(t1: C, t2: C, t3: D) => void' is not assignable to parameter of type '(t: C, t1: C) => void'. +!!! error TS2345: Argument of type '(t1: any, t2: any, t3: D) => void' is not assignable to parameter of type '(t: any, t1: any) => void'. \ No newline at end of file diff --git a/tests/cases/compiler/contextualTypingOfTooShortOverloads.ts b/tests/cases/compiler/contextualTypingOfTooShortOverloads.ts new file mode 100644 index 0000000000000..6fa88a1e5595a --- /dev/null +++ b/tests/cases/compiler/contextualTypingOfTooShortOverloads.ts @@ -0,0 +1,49 @@ +// small repro from #11875 +var use: Overload; +use((req, res) => {}); + +interface Overload { + (handler1: (req1: string) => void): void; + (handler2: (req2: number, res2: number) => void): void; +} +// larger repro from #11875 +let app: MyApp; +app.use((err: any, req, res, next) => { return; }); + + +interface MyApp { + use: IRouterHandler & IRouterMatcher; +} + +interface IRouterHandler { + (...handlers: RequestHandler[]): T; + (...handlers: RequestHandlerParams[]): T; +} + +interface IRouterMatcher { + (path: PathParams, ...handlers: RequestHandler[]): T; + (path: PathParams, ...handlers: RequestHandlerParams[]): T; +} + +type PathParams = string | RegExp | (string | RegExp)[]; +type RequestHandlerParams = RequestHandler | ErrorRequestHandler | (RequestHandler | ErrorRequestHandler)[]; + +interface RequestHandler { + (req: Request, res: Response, next: NextFunction): any; +} + +interface ErrorRequestHandler { + (err: any, req: Request, res: Response, next: NextFunction): any; +} + +interface Request { + method: string; +} + +interface Response { + statusCode: number; +} + +interface NextFunction { + (err?: any): void; +}