Skip to content
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
12 changes: 8 additions & 4 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ export const enum ContainerFlags {
HasLocals = 1 << 5,
IsInterface = 1 << 6,
IsObjectLiteralOrClassExpressionMethodOrAccessor = 1 << 7,
PropagatesThisKeyword = 1 << 8,
}

/** @internal */
Expand Down Expand Up @@ -1055,7 +1056,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
currentExceptionTarget = saveExceptionTarget;
activeLabelList = saveActiveLabelList;
hasExplicitReturn = saveHasExplicitReturn;
seenThisKeyword = node.kind === SyntaxKind.ArrowFunction ? saveSeenThisKeyword || seenThisKeyword : saveSeenThisKeyword;
seenThisKeyword = containerFlags & ContainerFlags.PropagatesThisKeyword ? saveSeenThisKeyword || seenThisKeyword : saveSeenThisKeyword;
}
else if (containerFlags & ContainerFlags.IsInterface) {
const saveSeenThisKeyword = seenThisKeyword;
Expand Down Expand Up @@ -3845,23 +3846,26 @@ export function getContainerFlags(node: Node): ContainerFlags {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ClassStaticBlockDeclaration:
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;

case SyntaxKind.MethodSignature:
case SyntaxKind.CallSignature:
case SyntaxKind.JSDocSignature:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructSignature:
case SyntaxKind.ConstructorType:
return ContainerFlags.IsContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.PropagatesThisKeyword;

case SyntaxKind.JSDocImportTag:
// treat as a container to prevent using an enclosing effective host, ensuring import bindings are scoped correctly
return ContainerFlags.IsContainer | ContainerFlags.HasLocals;
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.PropagatesThisKeyword;

case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsFunctionExpression;

case SyntaxKind.ArrowFunction:
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsFunctionExpression | ContainerFlags.PropagatesThisKeyword;

case SyntaxKind.ModuleBlock:
return ContainerFlags.IsControlFlowContainer;

Expand Down
52 changes: 52 additions & 0 deletions tests/baselines/reference/controlFlowForFunctionLike1.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
controlFlowForFunctionLike1.ts(10,12): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
controlFlowForFunctionLike1.ts(31,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.


==== controlFlowForFunctionLike1.ts (2 errors) ====
function test1(a: number | string) {
if (typeof a === "number") {
const fn = (arg: typeof a) => true;
return fn;
}
return;
}

test1(0)?.(100);
test1(0)?.("");
~~
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

function test2(a: number | string) {
if (typeof a === "number") {
const fn: { (arg: typeof a): boolean; } = () => true;
return fn;
}
return;
}

test2(0)?.(100);
test2(0)?.("");

function test3(a: number | string) {
if (typeof a === "number") {
return (arg: typeof a) => {};
}
throw new Error("");
}

test3(1)(100);
test3(1)("");
~~
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

function test4(a: number | string) {
let fn = (arg: typeof a) => {};
if (Math.random() && typeof a === "number") {
return fn;
}
throw new Error("");
}

test4(1)?.(100);
test4(1)?.("");

100 changes: 100 additions & 0 deletions tests/baselines/reference/controlFlowForFunctionLike1.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//// [tests/cases/compiler/controlFlowForFunctionLike1.ts] ////

=== controlFlowForFunctionLike1.ts ===
function test1(a: number | string) {
>test1 : Symbol(test1, Decl(controlFlowForFunctionLike1.ts, 0, 0))
>a : Symbol(a, Decl(controlFlowForFunctionLike1.ts, 0, 15))

if (typeof a === "number") {
>a : Symbol(a, Decl(controlFlowForFunctionLike1.ts, 0, 15))

const fn = (arg: typeof a) => true;
>fn : Symbol(fn, Decl(controlFlowForFunctionLike1.ts, 2, 9))
>arg : Symbol(arg, Decl(controlFlowForFunctionLike1.ts, 2, 16))
>a : Symbol(a, Decl(controlFlowForFunctionLike1.ts, 0, 15))

return fn;
>fn : Symbol(fn, Decl(controlFlowForFunctionLike1.ts, 2, 9))
}
return;
}

test1(0)?.(100);
>test1 : Symbol(test1, Decl(controlFlowForFunctionLike1.ts, 0, 0))

test1(0)?.("");
>test1 : Symbol(test1, Decl(controlFlowForFunctionLike1.ts, 0, 0))

function test2(a: number | string) {
>test2 : Symbol(test2, Decl(controlFlowForFunctionLike1.ts, 9, 15))
>a : Symbol(a, Decl(controlFlowForFunctionLike1.ts, 11, 15))

if (typeof a === "number") {
>a : Symbol(a, Decl(controlFlowForFunctionLike1.ts, 11, 15))

const fn: { (arg: typeof a): boolean; } = () => true;
>fn : Symbol(fn, Decl(controlFlowForFunctionLike1.ts, 13, 9))
>arg : Symbol(arg, Decl(controlFlowForFunctionLike1.ts, 13, 17))
>a : Symbol(a, Decl(controlFlowForFunctionLike1.ts, 11, 15))

return fn;
>fn : Symbol(fn, Decl(controlFlowForFunctionLike1.ts, 13, 9))
}
return;
}

test2(0)?.(100);
>test2 : Symbol(test2, Decl(controlFlowForFunctionLike1.ts, 9, 15))

test2(0)?.("");
>test2 : Symbol(test2, Decl(controlFlowForFunctionLike1.ts, 9, 15))

function test3(a: number | string) {
>test3 : Symbol(test3, Decl(controlFlowForFunctionLike1.ts, 20, 15))
>a : Symbol(a, Decl(controlFlowForFunctionLike1.ts, 22, 15))

if (typeof a === "number") {
>a : Symbol(a, Decl(controlFlowForFunctionLike1.ts, 22, 15))

return (arg: typeof a) => {};
>arg : Symbol(arg, Decl(controlFlowForFunctionLike1.ts, 24, 12))
>a : Symbol(a, Decl(controlFlowForFunctionLike1.ts, 22, 15))
}
throw new Error("");
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --))
}

test3(1)(100);
>test3 : Symbol(test3, Decl(controlFlowForFunctionLike1.ts, 20, 15))

test3(1)("");
>test3 : Symbol(test3, Decl(controlFlowForFunctionLike1.ts, 20, 15))

function test4(a: number | string) {
>test4 : Symbol(test4, Decl(controlFlowForFunctionLike1.ts, 30, 13))
>a : Symbol(a, Decl(controlFlowForFunctionLike1.ts, 32, 15))

let fn = (arg: typeof a) => {};
>fn : Symbol(fn, Decl(controlFlowForFunctionLike1.ts, 33, 5))
>arg : Symbol(arg, Decl(controlFlowForFunctionLike1.ts, 33, 12))
>a : Symbol(a, Decl(controlFlowForFunctionLike1.ts, 32, 15))

if (Math.random() && typeof a === "number") {
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>a : Symbol(a, Decl(controlFlowForFunctionLike1.ts, 32, 15))

return fn;
>fn : Symbol(fn, Decl(controlFlowForFunctionLike1.ts, 33, 5))
}
throw new Error("");
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --))
}

test4(1)?.(100);
>test4 : Symbol(test4, Decl(controlFlowForFunctionLike1.ts, 30, 13))

test4(1)?.("");
>test4 : Symbol(test4, Decl(controlFlowForFunctionLike1.ts, 30, 13))

Loading