Allow inferring a type alias for derived return types
See original GitHub issueSuggestion
đ Search Terms
function inferred return type alias
â 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
It would be extremely convenient if a function were able to declare a type alias for its return type inline. A possible syntax for this could be:
const createCounter = (): infer Counter => {
const result = {
count: 0,
increment: () => ++result.count,
};
return result;
};
This would create a type alias automatically, allowing usage like:
import { Counter, createCounter } from './counter';
đ Motivating Example
Currently, a class declaration automatically emits a type for the resulting constructor function. However, if an object is created with a standard function, this is not possible. The inferred return type of functions can be aliased like:
// aliased type:
// type Counter = {
// get: () => number;
// increment: () => number;
// }
export type Counter = ReturnType<typeof createCounter>;
const createCounter = () => {
const result = {
count: 0,
increment: () => ++result.count,
};
return result;
};
This is an powerful pattern, as it allows the implementation to be the source of reference for the type, rather than the other way around. However, while the aliased type is derived from the function, the function itself does not return the aliased type.
// type is:
// const counter: {
// get: () => number;
// increment: () => number;
// }
// NOT Counter
const counter = createCounter();
In order to use the derived type, there are 2 options, both of which are clumsy:
- Put the onus on the caller to explicitly use the type:
import { Counter, createCounter } from './counter';
const counter: Counter = createCounter();
- Create a superfluous higher order function:
export type Counter = ReturnType<typeof createCounterInternal>;
export const createCounter = (): Counter => createCounterInternal();
const createCounterInternal = () => {
// counter impl
};
đť Use Cases
Any function that returns a complex object could benefit from this.
Issue Analytics
- State:
- Created a year ago
- Reactions:31
- Comments:6
Top GitHub Comments
Admittedly, I donât know how much complexity this is under the hood, but as a language feature, it would be particularly nice if it could be used to alias the internals of a generic type, promises being the most obvious case:
The semantics of the
infer
keyword already exist with conditional types:While there is no formal definition in the docs, one might be:
The semantics would remain unchanged. This merely alters the language syntax to allow it in a different place.
It already is. This change would merely add one additional allowable location which would be function return types.
The mental model should be the same as a
class
. Stripping away the syntactic sugar of ES6 classes, a class is really just a constructor function. TypeScript provides additional syntactic sugar the make it define:Declaring a class gives access to both. Classes are quite limited, however, and this would allow similar convenience to âcreatorâ functions, which are far more flexible.
That would completely defeat the purpose. Callers would be unable to use the alias, only the anonymous inferred type, so exporting the function without exporting the type alias would be exactly the same as omitting the type.
When you declare a class, the value and the type always share a lexical scope. Imagine if they didnât, and instead required an explicit
export type ClassName
Definitely not. Itâs conceptually inverted. Inference on the âleft handâ of an assignment statement is inferring the type of the variable, not describing the type of the expressionâs result. The âright handâ expression is evaluated first and always produces a result of a specific, unambiguous type. Even if itâs anonymous,
any
, orunknown
, itâs still a value with a concrete type. If you happen to assign it to a variable, the variableâs type is a reflection of that valueâs type, not a definition for it.someFun
âownsâ the type here. Any variable assigned to its result should be that type. The alias produces confusion, not clarity. The only reason you would want an alias on the consumer side is ifsomeFun
is external code with a missing or incorrect type. Even then, youâre better off wrapping the function in another that defines a better type:Then assigning variables to that âmore correctâ type: