-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Description
Search Terms
Controlled runtime inference for trusted pure functions
Viability Checklist
- 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 isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
Suggestion
Summary
A proposed enhancement to TypeScript’s static analysis that allows developers to explicitly permit runtime evaluation of pure functions for safer and stronger type inference.
Motivating Example
Basic Example
const config = secureInference(() => ({
defaultLimit: parseInt(process.env.DEFAULT_LIMIT || '7'),
}));
// Now this will be inferred as: number
const defaultLimit = config.defaultLimit;
This tells TypeScript: “You can safely run this code at compile-time (or trusted runtime) because I’m explicitly opting into it.”
Motivation
In large-scale applications, especially with environment-based configuration, it’s common to use runtime values that TypeScript can’t infer statically. Developers end up writing fallback logic or manually asserting types:
const limit: number = configService.get<number>('defaultLimit') ?? 7;
Despite already providing default values during initialization, TypeScript can't guarantee the inferred type is non-undefined
.
This leads to:
- Redundant fallbacks.
- Unnecessary boilerplate.
- Frustration with TypeScript’s limitations when types are logically safe.
Detailed Design
Introduce a new keyword or helper (e.g. secureInference
) that wraps pure functions whose result is safe to evaluate once, either at build-time or controlled runtime.
A secureInference
-wrapped function must:
- Be a pure function (no side effects).
- Return serializable values (e.g. primitives, arrays, objects).
- Not depend on complex closures or async operations.
The compiler could run the function once and treat the returned value as fully inferred.
Drawbacks
- Adds an evaluation step that could be misused if the function isn't pure.
- Requires tooling updates to evaluate and cache values safely.
- Might increase complexity for the compiler/runtime boundary.
Alternatives
- Keep using fallback values or custom wrappers.
- Use type assertions (but these reduce type safety).
- Rely on code generation or static config files.
Adoption Strategy
secureInference
could be introduced as an opt-in helper via the TypeScript standard library or a plugin. Type-checking would still default to static-only, unless explicitly enabled.
Use Cases
1. What do you want to use this for?
The goal of secureInference
is to allow developers to opt into runtime evaluation for pure functions whose return values are needed for precise type inference. This is especially useful when:
- Loading configuration values at startup (e.g., from environment variables).
- Computing default values that are deterministic and safe to run once.
- Performing static transformations or mappings that TypeScript cannot evaluate without execution.
2. What shortcomings exist with current approaches?
Today, TypeScript does not evaluate functions or expressions at runtime for type inference. This leads to several common problems:
- Default values inside configuration functions are not inferred as non-
undefined
, even when fallback values are clearly provided. - Developers must either manually assert types or create redundant wrapper logic.
- Type safety is weakened or code becomes verbose due to repeated checks, especially in strongly typed applications.
Example:
const config = {
limit: parseInt(process.env.DEFAULT_LIMIT || '5'), // always a number
};
const limit: number = config.limit ?? 5; // redundant fallback
3. What workarounds are you using in the meantime?
Most developers currently use a mix of:
- Manual type annotations or type assertions (
as number
). - Wrapper functions or service layers that return strongly typed results.
- Custom runtime validation libraries (e.g.,
zod
,joi
) to ensure values conform to expectations.
While these work, they introduce boilerplate and force duplication of intent: once at runtime and again in the type system. secureInference
would reduce this friction by giving developers a clear and explicit way to tell TypeScript: “It’s safe to trust the result of this pure function for inference.”