Conversation
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
Perhaps, for better type-safety
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType?: TypeNode | undefined, | |
| rejectsType?: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/factory/nodeFactory.ts
Outdated
| throwsType?: TypeNode, | ||
| rejectsType?: TypeNode, |
There was a problem hiding this comment.
| throwsType?: TypeNode, | |
| rejectsType?: TypeNode, | |
| throwsType: TypeNode | undefined, | |
| rejectsType: TypeNode | undefined, |
src/compiler/parser.ts
Outdated
| return false; | ||
| } | ||
|
|
||
| function parseEffectClause(): { throwsType?: TypeNode; rejectsType?: TypeNode } { |
There was a problem hiding this comment.
| function parseEffectClause(): { throwsType?: TypeNode; rejectsType?: TypeNode } { | |
| function parseEffectClause(): { throwsType: TypeNode | undefined; rejectsType: TypeNode | undefined } { |
src/compiler/program.ts
Outdated
| } from "./_namespaces/ts.js"; | ||
| import * as performance from "./_namespaces/ts.performance.js"; | ||
|
|
||
| const THROWS_ERROR_CODES = new Set([18063, 18064]); |
There was a problem hiding this comment.
Perhaps find a better place to put this to separate it from the business logic
| * Test for whether a single line comment with leading whitespace trimmed's text contains a directive. | ||
| */ | ||
| const commentDirectiveRegExSingleLine = /^\/\/\/?\s*@(ts-expect-error|ts-ignore)/; | ||
| const commentDirectiveRegExSingleLine = /^\/\/\/?\s*@(ts-expect-error|ts-expect-exception|ts-ignore)/; |
There was a problem hiding this comment.
Note: this new directive name must not regex match with an existing one (e.g. ts-ignore-exception also matches with ts-ignore, which ignores the next line altogether (unwanted - we only want to disable exception checking)
src/compiler/program.ts
Outdated
| return fileEmitMode === ModuleKind.CommonJS ? ModuleKind.CommonJS : | ||
| emitModuleKindIsNonNodeESM(fileEmitMode) || fileEmitMode === ModuleKind.Preserve ? ModuleKind.ESNext : | ||
| undefined; | ||
| undefined; |
There was a problem hiding this comment.
Note: through the rest of the PR, will need to recheck formatting and lint to correct some of these issues and reduce the diff
d81d1b2 to
e23f866
Compare
…urrent state of JS ecosystem
Inferred Checked Errors — Proof of Concept Specification
Related GitHub Issue: microsoft#13219
1. Purpose
This feature introduces checked error semantics to TypeScript. Effects may be inferred from bodies OR declared at declaration sites via
throws/rejects. Inference remains the default; declarations exist primarily for.d.tsboundaries and (optionally) as contracts for.ts.The goal is to experimentally evaluate:
This is a compiler-level feature and cannot be implemented as an ESLint rule or language service plugin.
2. High-Level Behavior
The system enforces that:
Thrown/reject effects come from inference (see Core Semantics) or from declaration-site
throws/rejects(see Declaration-Site Effects). When inferring, sources include:throwstatements.d.tsor declared on.tssignatures)3. Core Semantics
3.1 Thrown Type Inference
Rule 1 —
throw exprContributes the type:
TypeOf(expr).Examples:
There is no restriction on thrown types.
Rule 2 —
throw;(rethrow)Inside a
catch (e)block:Contributes:
TypeOf(e).3.2 Function Thrown Type
For a function
f, its inferred thrown type is:If a cycle is detected in call graph analysis:
ThrownType = unknownThis is an intentional PoC simplification.
3.3 Call Site Enforcement
If a call expression may throw type
E, then it must satisfy one of:trythat has acatch.catch(...)void(see async section)Otherwise, a compile-time error is produced:
3.4 Declaration-Site Effects (
throws/rejects)Function, method, and constructor signatures may include:
throws E— for synchronous exceptionsrejects E— for Promise rejectionsThese clauses are allowed in
.d.tsand in.ts.Precedence:
.d.ts) → the declared clause is the only source of truth (no inference).4. try/catch/finally Semantics
4.1 Absorption Rule
For:
Define:
T_try= thrown type of TRYT_catch= thrown type of CATCHT_finally= thrown type of FINALLYThen:
Thrown(tryStatement) = T_catch | T_finally—T_tryis considered handled (absorbed).Thrown(tryStatement) = T_try | T_finally4.2 Catch Variable Typing
For:
The type of
eis: union of all types thrown from TRY.Example:
This type supports standard TypeScript narrowing (e.g.
instanceof).5. Async / Promise Semantics
5.1 Async Functions
For:
If the function body infers thrown type
E, thenf(): Promise<T>is considered a promise that may reject withE. This is treated as an effect attached to the promise value.5.2 Enforcement on await
For:
If
expris a promise that may reject with typeE, then:awaitmust be inside a try/catch, orOtherwise compile error: unhandled rejection type E.
5.3 Fire-and-Forget (Stance 2)
The following are allowed patterns:
Explicit ignore
This suppresses enforcement.
Explicit catch
Any
.catch(...)counts as handling.Disallowed
Unless explicitly ignored or handled.
5.4 Promise.all
If:
p1rejectsE1p2rejectsE2Then
Promise.all([p1, p2])producesPromise<...>rejecting with(E1 | E2).Thus:
Requires handling of
E1 | E2.6. Standard Library and Declaration Files
.d.tsfunctions can declarethrows/rejectsat the declaration site; that is the primary mechanism for effects without a body. Curated stdlib mapping is optional as a bootstrap/migration tool, not the core plan—e.g. a small map may be used to seed declarations or for migration, but the main path is declaration-site effects in.d.ts. For declarations with no body and no declared clause, behavior is implementation-defined (e.g.neveror conservativeunknown; must be consistent).7. Recursion Handling
If call graph analysis detects recursion:
ThrownType = unknownThis avoids fixpoint complexity in the PoC. Future improvements may replace this with a recursive type marker.
8. Non-Goals (PoC Scope)
The following are explicitly out of scope:
9. Compile-Time Errors Introduced
New error class:
And for async:
These errors occur when:
.catchorvoid10. Expected Developer Experience
Before
After
Or propagate:
Async
Or explicitly ignore:
11. Design Principles
throws/rejects; inference remains the default; declarations exist primarily for.d.tsboundaries and (optionally) as contracts for.ts.unknown.12. Intended Outcome of the PoC
This feature should allow evaluation of:
void) is sufficient ergonomically.