Skip to content

Extract function types from function and arrow expressions. #60234

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
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
3 changes: 1 addition & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6174,8 +6174,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
enterNewScope(context, node) {
if (isFunctionLike(node) || isJSDocSignature(node)) {
const signature = getSignatureFromDeclaration(node);
const expandedParams = getExpandedParameters(signature, /*skipUnionExpanding*/ true)[0];
return enterNewScope(context as NodeBuilderContext, node, expandedParams, signature.typeParameters);
return enterNewScope(context as NodeBuilderContext, node, signature.parameters, signature.typeParameters);
}
else {
const typeParameters = isConditionalTypeNode(node) ? getInferTypeParameters(node) :
Expand Down
18 changes: 10 additions & 8 deletions src/compiler/expressionToTypeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -969,14 +969,16 @@ export function createSyntacticTypeNodeBuilder(
return failed;
}
function typeFromFunctionLikeExpression(fnNode: FunctionExpression | ArrowFunction, context: SyntacticTypeNodeBuilderContext) {
// Disable any inference fallback since we won't actually use the resulting type and we don't want to generate errors
const oldNoInferenceFallback = context.noInferenceFallback;
context.noInferenceFallback = true;
createReturnFromSignature(fnNode, /*symbol*/ undefined, context);
reuseTypeParameters(fnNode.typeParameters, context);
fnNode.parameters.map(p => ensureParameter(p, context));
context.noInferenceFallback = oldNoInferenceFallback;
return notImplemented;
const returnType = createReturnFromSignature(fnNode, /*symbol*/ undefined, context);
const typeParameters = reuseTypeParameters(fnNode.typeParameters, context);
const parameters = fnNode.parameters.map(p => ensureParameter(p, context));
return syntacticResult(
factory.createFunctionTypeNode(
typeParameters,
parameters,
returnType,
),
);
}
function canGetTypeFromArrayLiteral(arrayLiteral: ArrayLiteralExpression, context: SyntacticTypeNodeBuilderContext, isConstContext: boolean) {
if (!isConstContext) {
Expand Down
20 changes: 10 additions & 10 deletions tests/baselines/reference/arrowFunctionExpressions.types
Original file line number Diff line number Diff line change
Expand Up @@ -245,15 +245,15 @@ class MyClass {
// Arrow function used in arrow function
var arrrr = () => (m: number) => () => (n: number) => m + n;
>arrrr : () => (m: number) => () => (n: number) => number
> : ^^^^^^^ ^^ ^^^^^^^^^^^^ ^^ ^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^
>() => (m: number) => () => (n: number) => m + n : () => (m: number) => () => (n: number) => number
> : ^^^^^^^ ^^ ^^^^^^^^^^^^ ^^ ^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^
>(m: number) => () => (n: number) => m + n : (m: number) => () => (n: number) => number
> : ^ ^^ ^^^^^^^^^^^^ ^^ ^^^^^^^^^^^
> : ^ ^^ ^^^^^^^^^^^^ ^^^^^^^^^^^
>m : number
> : ^^^^^^
>() => (n: number) => m + n : () => (n: number) => number
> : ^^^^^^^ ^^ ^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^
>(n: number) => m + n : (n: number) => number
> : ^ ^^ ^^^^^^^^^^^
>n : number
Expand All @@ -273,11 +273,11 @@ var e = arrrr()(3)()(4);
>arrrr()(3)() : (n: number) => number
> : ^ ^^ ^^^^^^^^^^^
>arrrr()(3) : () => (n: number) => number
> : ^^^^^^^ ^^ ^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^
>arrrr() : (m: number) => () => (n: number) => number
> : ^ ^^ ^^^^^^^^^^^^ ^^ ^^^^^^^^^^^
> : ^ ^^ ^^^^^^^^^^^^ ^^^^^^^^^^^
>arrrr : () => (m: number) => () => (n: number) => number
> : ^^^^^^^ ^^ ^^^^^^^^^^^^ ^^ ^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^
>3 : 3
> : ^
>4 : 4
Expand All @@ -294,9 +294,9 @@ function someFn() {

var arr = (n: number) => (p: number) => p * n;
>arr : (n: number) => (p: number) => number
> : ^ ^^ ^^^^^^ ^^ ^^^^^^^^^^^
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^
>(n: number) => (p: number) => p * n : (n: number) => (p: number) => number
> : ^ ^^ ^^^^^^ ^^ ^^^^^^^^^^^
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^
>n : number
> : ^^^^^^
>(p: number) => p * n : (p: number) => number
Expand All @@ -320,7 +320,7 @@ function someFn() {
>arr(3) : (p: number) => number
> : ^ ^^ ^^^^^^^^^^^
>arr : (n: number) => (p: number) => number
> : ^ ^^ ^^^^^^ ^^ ^^^^^^^^^^^
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^
>3 : 3
> : ^
>4 : 4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function foo1(y = class {c = x}, x = 1) {
// ok - used in file
function foo2(y = function(x: typeof z) {}, z = 1) {
>foo2 : (y?: (x: typeof z) => void, z?: number) => void
> : ^ ^^^^ ^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
> : ^ ^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
>y : (x: typeof z) => void
> : ^ ^^ ^^^^^^^^^
>function(x: typeof z) {} : (x: typeof z) => void
Expand Down
20 changes: 10 additions & 10 deletions tests/baselines/reference/collisionSuperAndParameter.types
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ class Foo {

var lamda = (_super: number) => { // No Error
>lamda : (_super: number) => (x: any) => this
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^^^
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^
>(_super: number) => { // No Error return x => this; // New scope. So should inject new _this capture } : (_super: number) => (x: any) => this
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^^^
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^
>_super : number
> : ^^^^^^

Expand All @@ -33,9 +33,9 @@ class Foo {

var lambda = () => {
>lambda : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^
>() => { return x => this; // New scope. So should inject new _this capture } : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^

return x => this; // New scope. So should inject new _this capture
>x => this : (x: any) => this
Expand Down Expand Up @@ -64,9 +64,9 @@ class Foo2 extends Foo {

var lamda = (_super: number) => { // Error
>lamda : (_super: number) => (x: any) => this
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^^^
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^
>(_super: number) => { // Error return x => this; // New scope. So should inject new _this capture } : (_super: number) => (x: any) => this
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^^^
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^
>_super : number
> : ^^^^^^

Expand All @@ -86,9 +86,9 @@ class Foo2 extends Foo {

var lambda = () => {
>lambda : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^
>() => { return x => this; // New scope. So should inject new _this capture } : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^

return x => this; // New scope. So should inject new _this capture
>x => this : (x: any) => this
Expand Down Expand Up @@ -218,9 +218,9 @@ class Foo4 extends Foo {

var lambda = () => {
>lambda : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^
>() => { return x => this; // New scope. So should inject new _this capture } : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^

return x => this; // New scope. So should inject new _this capture
>x => this : (x: any) => this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Foo {

function inner() {
>inner : () => (x: any) => any
> : ^^^^^^^ ^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^

console.log(_this); // Error as this doesnt not resolve to user defined _this
>console.log(_this) : any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Foo {

function inner(_this: number) { // Error
>inner : (_this: number) => (x: any) => any
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^^
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^
>_this : number
> : ^^^^^^

Expand All @@ -34,9 +34,9 @@ class Foo {

var lamda = (_this: number) => { // Error
>lamda : (_this: number) => (x: any) => this
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^^^
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^
>(_this: number) => { // Error return x => this; // New scope. So should inject new _this capture } : (_this: number) => (x: any) => this
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^^^
> : ^ ^^ ^^^^^^ ^^^^^^^^^^^^
>_this : number
> : ^^^^^^

Expand All @@ -56,9 +56,9 @@ class Foo {

var lambda = () => {
>lambda : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^
>() => { return x => this; // New scope. So should inject new _this capture } : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^

return x => this; // New scope. So should inject new _this capture
>x => this : (x: any) => this
Expand Down Expand Up @@ -257,9 +257,9 @@ class Foo3 {

var lambda = () => {
>lambda : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^
>() => { return x => this; // New scope. So should inject new _this capture } : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^

return x => this; // New scope. So should inject new _this capture
>x => this : (x: any) => this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ class Foo2 {

var lambda = () => {
>lambda : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^
>() => { return x => this; // New scope. So should inject new _this capture } : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^

return x => this; // New scope. So should inject new _this capture
>x => this : (x: any) => this
Expand All @@ -35,9 +35,9 @@ class Foo3 {

var lambda = () => {
>lambda : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^
>() => { return x => this; // New scope. So should inject new _this capture } : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^

return x => this; // New scope. So should inject new _this capture
>x => this : (x: any) => this
Expand Down Expand Up @@ -66,9 +66,9 @@ class Foo4 {

var lambda = () => {
>lambda : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^
>() => { return x => this; // New scope. So should inject new _this capture } : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^

return x => this; // New scope. So should inject new _this capture
>x => this : (x: any) => this
Expand Down Expand Up @@ -97,9 +97,9 @@ class Foo5 {

var lambda = () => {
>lambda : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^
>() => { return x => this; // New scope. So should inject new _this capture } : () => (x: any) => this
> : ^^^^^^^ ^^^^^^^^^^^^^^
> : ^^^^^^^ ^^^^^^^^^^^^

return x => this; // New scope. So should inject new _this capture
>x => this : (x: any) => this
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/commentsFunction.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ b: number): void;
/** fooFunc
* comment
*/
declare var fooFunc: (b: string) => string;
declare var lambdaFoo: (a: number, b: number) => number;
declare var lambddaNoVarComment: (a: number, b: number) => number;
declare var fooFunc: (/** fooFunctionValue param */ b: string) => string;
declare var lambdaFoo: (/**param a*/ a: number, /**param b*/ b: number) => number;
declare var lambddaNoVarComment: (/**param a*/ a: number, /**param b*/ b: number) => number;
declare function blah(a: string): void;
declare function blah2(a: string): void;
declare function blah3(a: string): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ var dot: <T, S>(f: (_: T) => S) => <U>(g: (_: U) => T) => (_: U) => S;

dot = <T, S>(f: (_: T) => S) => <U>(g: (_: U) => T): (r:U) => S => (x) => f(g(x));
>dot = <T, S>(f: (_: T) => S) => <U>(g: (_: U) => T): (r:U) => S => (x) => f(g(x)) : <T, S>(f: (_: T) => S) => <U>(g: (_: U) => T) => (r: U) => S
> : ^ ^^ ^^ ^^ ^^^^^^ ^^ ^^ ^^^^^
> : ^ ^^ ^^ ^^ ^^^^^^ ^^ ^^^^^
>dot : <T, S>(f: (_: T) => S) => <U>(g: (_: U) => T) => (_: U) => S
> : ^ ^^ ^^ ^^ ^^^^^
><T, S>(f: (_: T) => S) => <U>(g: (_: U) => T): (r:U) => S => (x) => f(g(x)) : <T, S>(f: (_: T) => S) => <U>(g: (_: U) => T) => (r: U) => S
> : ^ ^^ ^^ ^^ ^^^^^^ ^^ ^^ ^^^^^
> : ^ ^^ ^^ ^^ ^^^^^^ ^^ ^^^^^
>f : (_: T) => S
> : ^ ^^ ^^^^^
>_ : T
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/contextualTypingOfAccessors.types
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ var x: {

x = {
>x = { get foo() { return (n)=>n }, set foo(x) {}} : { foo: (n: any) => any; }
> : ^^^^^^^^ ^^^^^^^^^^^^^^^^
> : ^^^^^^^^ ^^^^^^^^^^^^^^
>x : { foo: (x: number) => number; }
> : ^^^^^^^ ^^^
>{ get foo() { return (n)=>n }, set foo(x) {}} : { foo: (n: any) => any; }
> : ^^^^^^^^ ^^^^^^^^^^^^^^^^
> : ^^^^^^^^ ^^^^^^^^^^^^^^

get foo() {
>foo : (n: any) => any
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/declFileTypeofFunction.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,6 @@ declare function b1(): typeof b1;
declare function foo(): typeof foo;
declare var foo1: typeof foo;
declare var foo2: typeof foo;
declare var foo3: () => /*elided*/ any;
declare var x: () => /*elided*/ any;
declare var foo3: () => () => /*elided*/ any;
declare var x: () => () => /*elided*/ any;
Comment on lines +73 to +74
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know why this is changing? It seems a little awkward for this self referential type to be printed one level deep like this rather than the single elided any (which would cause fewer downstream errors)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The base cause is that at the root level syntactic printing is now always given a try. And in this case the parameter list can be copied, it's just the return type that needs to fallback on type printing. I think I can revert this behavior.

Copy link
Contributor Author

@dragomirtitian dragomirtitian Oct 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into it and I don't think I can easily revert to the old behavior. The type checked printer only knows a type is too recursive to represent when it tries to print it. So when I see the function expression, I try to reuse types from the expression. When doing so, I discover the missing return type and fallback to type checker printing which then discovers the type is recursive.

What specifically is the worry in this case ? () => any and () => () => any are both bad, they both allow calling the function, so I don't really think the types are worse.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moreso if someone actually calls it and uses the result, that result will not be any and may cause follow-on errors. But I think this particular case is exceeding rare and weird anyway. Like, the function is clearly returning a function, so maybe that safety is better.

declare function foo5(x: number): (x: number) => number;
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ export type BoundedInteger<

export const toBoundedInteger =
>toBoundedInteger : <LowerBound extends number, UpperBound extends number>(bounds: { lowerBound: LowerBound; upperBound: UpperBound; }) => (n: number) => BoundedInteger<LowerBound, UpperBound>
> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^^^^^ ^^ ^^^^^
> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^^^^^ ^^^^^

<LowerBound extends number, UpperBound extends number>(bounds: {
><LowerBound extends number, UpperBound extends number>(bounds: { lowerBound: LowerBound; upperBound: UpperBound; }) => ( n: number ): BoundedInteger<LowerBound, UpperBound> => // Implementation doesn't matter here ({} as any) : <LowerBound extends number, UpperBound extends number>(bounds: { lowerBound: LowerBound; upperBound: UpperBound; }) => (n: number) => BoundedInteger<LowerBound, UpperBound>
> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^^^^^ ^^ ^^^^^
> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^^^^^ ^^^^^
>bounds : { lowerBound: LowerBound; upperBound: UpperBound; }
> : ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ function referencedInInferredType({ name: alias }: Named) {

function referencedInNestedFunction({ name: alias }: Named) {
>referencedInNestedFunction : ({ name: alias }: Named) => (p: typeof alias) => void
> : ^ ^^ ^^^^^^ ^^ ^^^^^^^^^
> : ^ ^^ ^^^^^^ ^^^^^^^^^
>name : any
> : ^^^
>alias : string
Expand Down
Loading