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
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.