You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
TypeScript does not recognize unreachable code after a method returning never is called outside of its defining scope #59138
Does this issue occur when all extensions are disabled?: Yes
VS Code Version: 1.89.1
OS Version: All
To Reproduce:
class SimpleThrower {
public throwError(): never {
throw new Error('');
}
public doSomething(): string {
this.throwError(); // Directly call the never-returning function
return 'Success'; // TypeScript recognizes that this line is unreachable
}
}
() => {
const example = new SimpleThrower();
console.log(example.doSomething()); // This will throw an error
example.throwError(); // Throws an error and stops execution
console.log('foobar'); // TypeScript does not recognize that this line is unreachable
}
TypeScript should recognize that the line console.log('foobar'); is unreachable after example.throwError(); is called, similar to how it recognizes the unreachable code within the doSomething method.
Actual behavior:
TypeScript does not mark the line console.log('foobar'); as unreachable after the example.throwError(); call in the global scope or within a different function scope.
I ran into this exact same issue today. (See playground)
constprocess={foo(): never{thrownewError('bar');},}asconst;functionfoo(): never{thrownewError('foo');}process.foo();console.log('hello');foo();// from this point on, the unreachable code is detectedconsole.log('world');
However, if I use an interface to define process, it does work correctly. (See playground)
interfaceIProcess{foo(): never;}constprocess: IProcess={foo(): never{thrownewError('bar');},}asconst;functionfoo(): never{thrownewError('foo');}process.foo();// from this point on, the unreachable code is detectedconsole.log('hello');foo();console.log('world');
But then again, it doesn't work if you use [key: string] as the key in the interface. (See playground)
interfaceIProcess{[key: string]: ()=>never;}constprocess: IProcess={foo(): never{thrownewError('bar');},}asconst;functionfoo(): never{thrownewError('foo');}process.foo();console.log('hello');foo();// from this point on, the unreachable code is detectedconsole.log('world');
functiongetThrowError(){returnfunctionthrowError(){thrownewError();};}constthrowError=getThrowError();throwError();// Should be unreachableconsole.log("test");
Actually I think it is a bug, it is not a just suggestion.
interfaceThrowable{throw(): never;}functionfn(value: string|Throwable): string{if(typeofvalue!=="string")value.throw();returnvalue;// ^^^^^^ - Type 'string | Throwable' is not assignable to type 'string'.// Type 'Throwable' is not assignable to type 'string'.}
interfaceThrowable{throw(): never;}declarefunctionfail(): never;constcontext={ fail };functionfn(value: string|Throwable): string{if(typeofvalue!=="string")context.fail();returnvalue;// ^^^^^^ - Type 'string | Throwable' is not assignable to type 'string'.// Type 'Throwable' is not assignable to type 'string'.}
Activity
SamVerschueren commentedon Aug 22, 2024
I ran into this exact same issue today. (See playground)
However, if I use an interface to define
process, it does work correctly. (See playground)But then again, it doesn't work if you use
[key: string]as the key in the interface. (See playground)amannn commentedon Oct 18, 2024
I ran into this problem too:
Playground
I couldn't get it to work with an interface either.
DScheglov commentedon Oct 22, 2024
Actually I think it is a bug, it is not a just suggestion.
Playground
But this code works well:
Playground
As well as the following one:
Playground
This code doesn't work again:
Playground
So, currently we have the patch with adding return before
throw():Playground
But it is definitelly better to fix the inconsistent behaviour.
Ryan Cavanaugh (@RyanCavanaugh), could you please help with raising the priority of this issue?
alecmev commentedon Jul 21, 2025
Duplicate of #60368, I think?