diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 20d075cdfc07f..f99da48e3885f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27532,12 +27532,31 @@ namespace ts { function instantiateContextualType(contextualType: Type | undefined, node: Node, contextFlags: ContextFlags | undefined): Type | undefined { if (contextualType && maybeTypeOfKind(contextualType, TypeFlags.Instantiable)) { const inferenceContext = getInferenceContext(node); - // If no inferences have been made, nothing is gained from instantiating as type parameters - // would just be replaced with their defaults similar to the apparent type. - if (inferenceContext && contextFlags! & ContextFlags.Signature && some(inferenceContext.inferences, hasInferenceCandidates)) { - // For contextual signatures we incorporate all inferences made so far, e.g. from return - // types as well as arguments to the left in a function call. - return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper); + if (inferenceContext && contextFlags! & ContextFlags.Signature) { + // If no inferences have been made, nothing is gained from instantiating as type parameters + // would just be replaced with their defaults similar to the apparent type. + if (some(inferenceContext.inferences, hasInferenceCandidates)) { + // For contextual signatures we incorporate all inferences made so far, e.g. from return + // types as well as arguments to the left in a function call. + return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper); + } + + const apparentType = mapType(contextualType, getApparentType, /*noReductions*/ true); + + // If the apparent type is any, we cannot lose any information by instantiating, + // so we might as well try and possibly get a useful type + if (apparentType === anyType) { + return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper); + } + else if((apparentType.flags & TypeFlags.Union)) { + const signatureList = getContextualSignatureListFromApparentTypes((apparentType as UnionType).types, node as FunctionExpression | ArrowFunction | MethodDeclaration); + + if (!signatureList) { + // At this point, the signature type is guaranteed to eventually collapse into any + // because it is a union of incompatible function signatures + return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper); + } + } } if (inferenceContext?.returnMapper) { // For other purposes (e.g. determining whether to produce literal types) we only @@ -27953,8 +27972,17 @@ namespace ts { if (!(type.flags & TypeFlags.Union)) { return getContextualCallSignature(type, node); } - let signatureList: Signature[] | undefined; const types = (type as UnionType).types; + const signatureList = getContextualSignatureListFromApparentTypes(types, node); + + // Result is union of signatures collected (return type is union of return types of this signature set) + if (signatureList) { + return signatureList.length === 1 ? signatureList[0] : createUnionSignature(signatureList[0], signatureList); + } + } + + function getContextualSignatureListFromApparentTypes(types: Type[], node: FunctionExpression | ArrowFunction | MethodDeclaration): Signature[] | undefined { + let signatureList: Signature[] | undefined; for (const current of types) { const signature = getContextualCallSignature(current, node); if (signature) { @@ -27972,10 +28000,7 @@ namespace ts { } } } - // Result is union of signatures collected (return type is union of return types of this signature set) - if (signatureList) { - return signatureList.length === 1 ? signatureList[0] : createUnionSignature(signatureList[0], signatureList); - } + return signatureList; } function checkSpreadExpression(node: SpreadElement, checkMode?: CheckMode): Type { diff --git a/tests/baselines/reference/genericInferenceDefaultTypeParameter.js b/tests/baselines/reference/genericInferenceDefaultTypeParameter.js new file mode 100644 index 0000000000000..9a877dc10d509 --- /dev/null +++ b/tests/baselines/reference/genericInferenceDefaultTypeParameter.js @@ -0,0 +1,17 @@ +//// [genericInferenceDefaultTypeParameter.ts] +type Type = { + a: (e: string) => void, + b: (e: number) => void, +} + +function f1(props: Type[T]): any { + return null +} + +f1((event) => { }) + +//// [genericInferenceDefaultTypeParameter.js] +function f1(props) { + return null; +} +f1(function (event) { }); diff --git a/tests/baselines/reference/genericInferenceDefaultTypeParameter.symbols b/tests/baselines/reference/genericInferenceDefaultTypeParameter.symbols new file mode 100644 index 0000000000000..be086bf05a247 --- /dev/null +++ b/tests/baselines/reference/genericInferenceDefaultTypeParameter.symbols @@ -0,0 +1,28 @@ +=== tests/cases/compiler/genericInferenceDefaultTypeParameter.ts === +type Type = { +>Type : Symbol(Type, Decl(genericInferenceDefaultTypeParameter.ts, 0, 0)) + + a: (e: string) => void, +>a : Symbol(a, Decl(genericInferenceDefaultTypeParameter.ts, 0, 13)) +>e : Symbol(e, Decl(genericInferenceDefaultTypeParameter.ts, 1, 8)) + + b: (e: number) => void, +>b : Symbol(b, Decl(genericInferenceDefaultTypeParameter.ts, 1, 27)) +>e : Symbol(e, Decl(genericInferenceDefaultTypeParameter.ts, 2, 8)) +} + +function f1(props: Type[T]): any { +>f1 : Symbol(f1, Decl(genericInferenceDefaultTypeParameter.ts, 3, 1)) +>T : Symbol(T, Decl(genericInferenceDefaultTypeParameter.ts, 5, 12)) +>Type : Symbol(Type, Decl(genericInferenceDefaultTypeParameter.ts, 0, 0)) +>props : Symbol(props, Decl(genericInferenceDefaultTypeParameter.ts, 5, 40)) +>Type : Symbol(Type, Decl(genericInferenceDefaultTypeParameter.ts, 0, 0)) +>T : Symbol(T, Decl(genericInferenceDefaultTypeParameter.ts, 5, 12)) + + return null +} + +f1((event) => { }) +>f1 : Symbol(f1, Decl(genericInferenceDefaultTypeParameter.ts, 3, 1)) +>event : Symbol(event, Decl(genericInferenceDefaultTypeParameter.ts, 9, 4)) + diff --git a/tests/baselines/reference/genericInferenceDefaultTypeParameter.types b/tests/baselines/reference/genericInferenceDefaultTypeParameter.types new file mode 100644 index 0000000000000..fe721f36560d4 --- /dev/null +++ b/tests/baselines/reference/genericInferenceDefaultTypeParameter.types @@ -0,0 +1,27 @@ +=== tests/cases/compiler/genericInferenceDefaultTypeParameter.ts === +type Type = { +>Type : { a: (e: string) => void; b: (e: number) => void; } + + a: (e: string) => void, +>a : (e: string) => void +>e : string + + b: (e: number) => void, +>b : (e: number) => void +>e : number +} + +function f1(props: Type[T]): any { +>f1 : (props: Type[T]) => any +>props : Type[T] + + return null +>null : null +} + +f1((event) => { }) +>f1((event) => { }) : any +>f1 : (props: Type[T]) => any +>(event) => { } : (event: string) => void +>event : string + diff --git a/tests/baselines/reference/genericInferenceDefaultTypeParameterJsxReact.js b/tests/baselines/reference/genericInferenceDefaultTypeParameterJsxReact.js new file mode 100644 index 0000000000000..3007c8e274b7d --- /dev/null +++ b/tests/baselines/reference/genericInferenceDefaultTypeParameterJsxReact.js @@ -0,0 +1,27 @@ +//// [genericInferenceDefaultTypeParameterJsxReact.tsx] +/// +import React, { ComponentPropsWithRef, ElementType, ReactNode } from 'react' + +type ButtonBaseProps = + ComponentPropsWithRef & { + children?: ReactNode +} + +function Component(props: ButtonBaseProps) { + return <> +} + +const v1 = e.preventDefault()} /> + +//// [genericInferenceDefaultTypeParameterJsxReact.js] +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +exports.__esModule = true; +/// +var react_1 = __importDefault(require("react")); +function Component(props) { + return react_1["default"].createElement(react_1["default"].Fragment, null); +} +var v1 = react_1["default"].createElement(Component, { onClick: function (e) { return e.preventDefault(); } }); diff --git a/tests/baselines/reference/genericInferenceDefaultTypeParameterJsxReact.symbols b/tests/baselines/reference/genericInferenceDefaultTypeParameterJsxReact.symbols new file mode 100644 index 0000000000000..359c8073793c1 --- /dev/null +++ b/tests/baselines/reference/genericInferenceDefaultTypeParameterJsxReact.symbols @@ -0,0 +1,42 @@ +=== tests/cases/compiler/genericInferenceDefaultTypeParameterJsxReact.tsx === +/// +import React, { ComponentPropsWithRef, ElementType, ReactNode } from 'react' +>React : Symbol(React, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 1, 6)) +>ComponentPropsWithRef : Symbol(ComponentPropsWithRef, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 1, 15)) +>ElementType : Symbol(ElementType, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 1, 38)) +>ReactNode : Symbol(ReactNode, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 1, 51)) + +type ButtonBaseProps = +>ButtonBaseProps : Symbol(ButtonBaseProps, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 1, 76)) +>T : Symbol(T, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 3, 21)) +>ElementType : Symbol(ElementType, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 1, 38)) + + ComponentPropsWithRef & { +>ComponentPropsWithRef : Symbol(ComponentPropsWithRef, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 1, 15)) +>T : Symbol(T, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 3, 21)) + + children?: ReactNode +>children : Symbol(children, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 31)) +>ReactNode : Symbol(ReactNode, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 1, 51)) +} + +function Component(props: ButtonBaseProps) { +>Component : Symbol(Component, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 6, 1)) +>T : Symbol(T, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 8, 19)) +>ElementType : Symbol(ElementType, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 1, 38)) +>props : Symbol(props, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 8, 51)) +>ButtonBaseProps : Symbol(ButtonBaseProps, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 1, 76)) +>T : Symbol(T, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 8, 19)) + + return <> +} + +const v1 = e.preventDefault()} /> +>v1 : Symbol(v1, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 12, 5)) +>Component : Symbol(Component, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 6, 1)) +>onClick : Symbol(onClick, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 12, 21)) +>e : Symbol(e, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 12, 31)) +>e.preventDefault : Symbol(React.SyntheticEvent.preventDefault, Decl(react16.d.ts, 642, 31)) +>e : Symbol(e, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 12, 31)) +>preventDefault : Symbol(React.SyntheticEvent.preventDefault, Decl(react16.d.ts, 642, 31)) + diff --git a/tests/baselines/reference/genericInferenceDefaultTypeParameterJsxReact.types b/tests/baselines/reference/genericInferenceDefaultTypeParameterJsxReact.types new file mode 100644 index 0000000000000..76f278db3770f --- /dev/null +++ b/tests/baselines/reference/genericInferenceDefaultTypeParameterJsxReact.types @@ -0,0 +1,36 @@ +=== tests/cases/compiler/genericInferenceDefaultTypeParameterJsxReact.tsx === +/// +import React, { ComponentPropsWithRef, ElementType, ReactNode } from 'react' +>React : typeof React +>ComponentPropsWithRef : any +>ElementType : any +>ReactNode : any + +type ButtonBaseProps = +>ButtonBaseProps : ButtonBaseProps + + ComponentPropsWithRef & { + children?: ReactNode +>children : React.ReactNode +} + +function Component(props: ButtonBaseProps) { +>Component : = "span">(props: ButtonBaseProps) => JSX.Element +>props : ButtonBaseProps + + return <> +><> : JSX.Element +} + +const v1 = e.preventDefault()} /> +>v1 : JSX.Element +> e.preventDefault()} /> : JSX.Element +>Component : = "span">(props: ButtonBaseProps) => JSX.Element +>onClick : (e: React.MouseEvent) => void +>e => e.preventDefault() : (e: React.MouseEvent) => void +>e : React.MouseEvent +>e.preventDefault() : void +>e.preventDefault : () => void +>e : React.MouseEvent +>preventDefault : () => void + diff --git a/tests/cases/compiler/genericInferenceDefaultTypeParameter.ts b/tests/cases/compiler/genericInferenceDefaultTypeParameter.ts new file mode 100644 index 0000000000000..7b1869099633a --- /dev/null +++ b/tests/cases/compiler/genericInferenceDefaultTypeParameter.ts @@ -0,0 +1,12 @@ +// @noImplicitAny: true + +type Type = { + a: (e: string) => void, + b: (e: number) => void, +} + +function f1(props: Type[T]): any { + return null +} + +f1((event) => { }) \ No newline at end of file diff --git a/tests/cases/compiler/genericInferenceDefaultTypeParameterJsxReact.tsx b/tests/cases/compiler/genericInferenceDefaultTypeParameterJsxReact.tsx new file mode 100644 index 0000000000000..09a0d654248bd --- /dev/null +++ b/tests/cases/compiler/genericInferenceDefaultTypeParameterJsxReact.tsx @@ -0,0 +1,17 @@ +// @noImplicitAny: true +// @esModuleInterop: true +// @jsx: react + +/// +import React, { ComponentPropsWithRef, ElementType, ReactNode } from 'react' + +type ButtonBaseProps = + ComponentPropsWithRef & { + children?: ReactNode +} + +function Component(props: ButtonBaseProps) { + return <> +} + +const v1 = e.preventDefault()} /> \ No newline at end of file