Open
Description
Suggestion
🔍 Search Terms
- JavaScript return never
- JavaScript method return never
- JavaScript override method return never
- JavaScript abstract method return never
✅ Viability Checklist
My suggestion meets these guidelines:
- [?] This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
⭐ Suggestion
When implementing an abstract method with a signature that returns never
, the concrete implementation should have its return type inferred as never
if the method body contains an explicit throw
and no return
statements including the implicit return
.
📃 Motivating Example
// @allowJs
// @checkJs
// @noEmit
// @noImplicitAny: false
// @target: ESNext
// @module: ESNext
// @lib: ESNext
// @filename: tokens.d.ts
// Based on: https://github.com/engine262/engine262/tree/main/src/parser/tokens.mjs
export enum Token {
// ...
EOS = 13,
// ...
}
// @filename: Lexer.d.ts
// Based on: https://github.com/engine262/engine262/tree/main/src/parser/Lexer.mjs
import { Token } from "./tokens.js";
export interface ParsedToken {
type: Token;
// ...
}
export abstract class Lexer {
// ...
peek(): ParsedToken;
// ...
abstract createSyntaxError(
context: object | number | undefined,
template: string,
templateArgs: readonly unknown[]
): SyntaxError;
abstract raiseEarly(
template: string,
context?: object | number,
...templateArgs: readonly unknown[]
): SyntaxError;
abstract raise(
// ^?
template: string,
context?: object | number,
...templateArgs: readonly unknown[]
): never;
abstract unexpected(
context?: object | number,
...templateArgs: readonly unknown[]
): never;
}
// @filename: Parser.js
// Based on: https://github.com/engine262/engine262/tree/main/src/parser/Parser.mjs
import { Lexer } from "./Lexer.js";
import { Token } from "./tokens.js";
export class Parser extends Lexer {
earlyErrors = new globalThis.Set();
createSyntaxError(context = this.peek(), template, templateArgs) {
if (template === "UnexpectedToken" && context.type === Token.EOS) {
template = "UnexpectedEOS";
}
// ...
return new globalThis.SyntaxError(/* ... */);
}
raiseEarly(template, context, ...templateArgs) {
const e = this.createSyntaxError(context, template, templateArgs);
this.earlyErrors.add(e);
return e;
}
// Expected return type: never
// Actual return type: void
raise(template, context, ...templateArgs) {
// ^?
throw this.createSyntaxError(context, template, templateArgs);
}
unexpected(context, ...templateArgs) {
return this.raise(context, "UnexpectedToken", templateArgs);
}
}
💻 Use Cases
To get better type inference while working on engine262’s parser.