Repository: TypeScript
Issue: Conditionally optional/conditionally readonly properties
Labels: Suggestion,Awaiting More Feedback

# Conditionally-Optional Properties (in object types and interfaces)

<!--
  Please fill in each section completely. Thank you!
-->

## 🔍 Search Terms

- conditionally optional
- conditional optional
- conditionally readonly
- conditional readonly


These issues are related, but not exactly the same:

- #36126
- #31409

## ✅ Viability Checklist

<!--
   Suggestions that don't meet all these criteria are very, very unlikely to be accepted.
   We always recommend reviewing the TypeScript design goals before investing time writing
   a proposal for ideas outside the scope of the project.
-->
My suggestion meets these guidelines:

* [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 feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).


## ⭐ Suggestion / 📃 Motivating Example

A new syntax and associated type-checking making it possible to mark a property as "conditionally-optional", for example:

```ts
interface Calculate<T> {
  items: T[];
  
  /**
   * Converts an item into a string for comparison.
   * If `T` is already a string type, this is optional.
   */  
  getKey(? if T extends string): (item: T) => string;
}
```

the intent here is that `getKey` is optional if `T` is `string` or a subtype of `string`, but otherwise `getKey` is mandatory. Thus, an implementation could (for example) provide a default implementation `getKey = str => str`.

It's currently possible to write types that accomplish this using a mixture of intersections and conditionally-mapped types. However, you lose certain important ergonomic attributes:

- the syntax for intersection types, mapped types, and conditional types is much more complicated and much less familiar than interface/object types
- conditional types and complex mappings are likely to lose jsdoc comments, defeating intellisense when developers are later trying to use the type
- complex types can't be `extend`ed whereas interfaces and object types can be

## 💻 Use Cases

In general, any attribute of a property could be made conditional in the same way:

```ts
type Example = <T, Flag extends boolean, Active extends boolean> = {
  // conditionally optional:
  convertToString (? if T extends string): (item: T) => string;

  // optional; made conditionally required:
  initialValue? (-? if T extends boolean): T; 

  // conditionally readonly:
  (readonly if Flag extends "permanent") flagValue: boolean;

  // conditionally defined:
  (parentId if Active extends true): string;
}
```

In most cases, a basic (but incomplete) workaround exists: use the less-restrictive form everywhere. For example, if it's going to mostly be _used_ and you don't want to forget to pass a property, just make it required; if it's mostly going to be accessed and you don't want to forget that a property is present, just make it optional, etc.

Allowing these values to be set conditionally would just make it easier to express certain more-complex domain-specific constraints, while keeping the types mostly self-contained and readable.