Repository: TypeScript
Issue: 'identity' modifier to indicate a function's parameter-less returns should be narrowed like a value
Labels: Needs More Info

### 🔍 Search Terms

indicate function returns identical value cached memoized signals

### ✅ Viability Checklist

- [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
- [x] This wouldn't change the runtime behavior of existing JavaScript code
- [x] This could be implemented without emitting different JS based on the types of the expressions
- [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- [x] This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- [x] This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals

### ⭐ Suggestion

Many apps today are built on the concept of using small "getter" functions as wrappers around values. For example, _Signals_ as implemented in [Angular](https://angular.dev/guide/signals "Angular Signals"), [Solid](https://docs.solidjs.com/concepts/signals "Solid Signals"), and the stage 1 [TC39 Signals proposal](https://github.com/tc39/proposal-signals) often look something like:

```ts
declare const value: () => string | undefined;

if (value() !== undefined) {
  console.log(value().toUpperCase());
}
```

Signals users have struggled with using them in TypeScript because, at present, there isn't a way to get that code block to type check without type errors. Signals users know that the result of `value()` must be `string` inside the `if`, but TypeScript doesn't have a way to note that the result should be type narrowed. Common workarounds today include `!`, `?.`, and refactoring to store intermediate values. All of which are at best unnecessary verbosity, and at worst conflict with frameworks.

Request: can we have a keyword -or, failing that, built-in / intrinsic type- to indicate that _calls to a function produce a referentially equal, structurally unchanging value_? In other words, that the function call (`value()`) should be treated by type narrowing as if it was just a variable reference (`value`)?

Proposal: how about an **`identity`** modifier keyword for function types that goes before the `()`? It would be treated in syntax space similarly to other modifier keywords such as `abstract` and `readonly`.

### 📃 Motivating Example

When an `identity` function is called, it is given the same type narrowing as variables. Code like this would now type check without type errors, as if `value` was declared as `const value: string | undefined`:

```ts
declare const value: identity () => string | undefined;

if (value() !== undefined) {
  value();
  // Before: string | undefined
  // Now: string

  console.log(value().toUpperCase());
  // Before:  ~~~~~~~ Object is possibly 'undefined'.
  // Now: ✅
}
```

Narrowing would be cleared the same as variables when, say, a new closure/scope can't be guaranteed to preserve narrowing:

```ts
declare const value: identity () => string | undefined;

if (value() !== undefined) {
  setTimeout(() => {
    value();
    // Still: string | undefined

    console.log(value().toUpperCase());
    // Still:   ~~~~~~~ Object is possibly 'undefined'.
  });
}
```

### 💻 Use Cases

One difficult-to-answer design question is: how could `identity` handle functions with parameters? I propose the modifier not be allowed on function signaturess with parameters to start. It should produce a type error for now. The vast majority of Signals users wouldn't need signatures with parameters, so I don't think solidifying that needs to block this proposal. IMO that can always be worked on later.

Furthermore, it's common for frameworks to set up functions with a parameter-less "getter" signature and a single-parameter "setter" signature. I propose for an initial version of the feature, calling any other methods or setting to any properties on the type should clear type narrowing:

```ts
declare const value: {
  identity (): string | undefined;
  (newValue: string | undefined): void;
}

if (value() !== undefined) {
  value("...");

  value();
  // Still: string | undefined

  console.log(value().toUpperCase());
  // Still:   ~~~~~~~ Object is possibly 'undefined'.
}
```

More details on the difficulties of signals with TypeScript:

* Angular: https://github.com/angular/angular/issues/49161
* Solid: https://github.com/solidjs/solid/discussions/1575
  * Also on Solid: https://github.com/microsoft/TypeScript/issues/57725#issuecomment-2573624234

If a new modifier keyword isn't palatable, a fallback proposal could be a built-in type like `Identity<T>`. This wouldn't be a new utility type ([FAQ: no new utility types](https://github.com/microsoft/typescript/wiki/faq#new-utility-types)); it'd be closer to the [built-in template string manipulation types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html#intrinsic-string-manipulation-types).