Skip to content

Skip overloads with too short function parameters #11905

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 28, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 = (<UnionType>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
Expand Down
59 changes: 59 additions & 0 deletions tests/baselines/reference/contextualTypingOfTooShortOverloads.js
Original file line number Diff line number Diff line change
@@ -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<this> & IRouterMatcher<this>;
}

interface IRouterHandler<T> {
(...handlers: RequestHandler[]): T;
(...handlers: RequestHandlerParams[]): T;
}

interface IRouterMatcher<T> {
(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; });
139 changes: 139 additions & 0 deletions tests/baselines/reference/contextualTypingOfTooShortOverloads.symbols
Original file line number Diff line number Diff line change
@@ -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<this> & IRouterMatcher<this>;
>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<T> {
>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<T> {
>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))
}

143 changes: 143 additions & 0 deletions tests/baselines/reference/contextualTypingOfTooShortOverloads.types
Original file line number Diff line number Diff line change
@@ -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<MyApp> & IRouterMatcher<MyApp>
>app : MyApp
>use : IRouterHandler<MyApp> & IRouterMatcher<MyApp>
>(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<this> & IRouterMatcher<this>;
>use : IRouterHandler<this> & IRouterMatcher<this>
>IRouterHandler : IRouterHandler<T>
>IRouterMatcher : IRouterMatcher<T>
}

interface IRouterHandler<T> {
>IRouterHandler : IRouterHandler<T>
>T : T

(...handlers: RequestHandler[]): T;
>handlers : RequestHandler[]
>RequestHandler : RequestHandler
>T : T

(...handlers: RequestHandlerParams[]): T;
>handlers : RequestHandlerParams[]
>RequestHandlerParams : RequestHandlerParams
>T : T
}

interface IRouterMatcher<T> {
>IRouterMatcher : IRouterMatcher<T>
>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
}

Loading