Repository: TypeScript
Issue: Allow marking functions in interfaces as callback / non-callable
Labels: Suggestion,Awaiting More Feedback

## Search Terms

callable function, non-callable function, callback function type, callback type modifier

## Suggestion

A keyword and some language support to identify functions which are not callable (they are only assignable callbacks).

## Use Cases

Currently, it is possible to call functions which aren't intended to be called, for example:

```ts
const btn = document.createElement('button')
btn.onclick = evt => {} // Correct use
btn.onclick(new MouseEvent('')) // Incorrect use, but no error
```

It should be possible to declare that `onclick` is only a callback, and thus you must not call it directly. A keyword such as `callback`, `noncallable` or `nocall` could be used for this purpose, in a spirit similar to the `readonly` modifier. For example:

```ts
interface HTMLButtonElement {
    callback onclick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
}
```

Similar capabilities to that of `readonly` should be implemented around it, such as being able to add and remove the modifier through mapped types:

```ts
type FullyCallable<T> = {
    -callback [P in keyof T]: T[P];
}
```

Which would set the stage for later usage in conditional types. For example, this is a way one can currently use to extract writable properties from a type:

```ts
type Not<T extends boolean> = T extends true ? false : true

type Equals<X, Y> =
    (<T> () => T extends X ? 1 : 2) extends
        (<T> () => T extends Y ? 1 : 2)
            ? true
            : false

type IsReadonly<O extends Record<any, any>, P extends keyof O> =
    Not<Equals<{[_ in P]: O[P]}, {-readonly [_ in P]: O[P]}>>

type WritableProperties<O extends Record<any, any>> = {
    [P in keyof O]: IsReadonly<O, P> extends true
        ? never
        : P
} extends {[_ in keyof O]: infer U}
    ? U
    : never

type Writable<O extends Record<any, any>> = Pick<O, WritableProperties<O>>

type T = Writable<{
    n: number,
    readonly b: boolean,
    o: {
        readonly s: string // Recursion is possible... But even crazier.
    }
}> // { n: number; o: { readonly s: string; }; }
```

It would be convenient to be able to do a similar thing with callbacks, identifying them and removing them at will while defining new types.

(A different topic altogether, but I hope declaring types like those above becomes more straightforward in the future, e.g. by having a direct way to identify `readonly` properties, or by being able to use logical operators as part of conditional types. Feel free to pick up on these ideas and start separate issues... Or I can do it myself if you ask me to. **Edit** I finally did: #31581, #31579)

## Example

The task I have at hand at the moment is to define the following button-spawning function (more generally, I would like to do this for any `Element`):

```ts
type ButtonProperties<T> = any

function button(properties: ButtonProperties<HTMLButtonElement>) {
    const btn = document.createElement('button')
    // Assign all `properties` to `btn`...
    return btn
}
```

But I would like to define `ButtonProperties<T>` in such a way that it removed from `HTMLButtonElement` properties meeting certain criteria, namely:
- Are readonly. (Managed.)
- Are index signatures. (Managed.)
- Are callbacks. (Sort-of managed, with the assumption that all callbacks defined in `Element`s take as a first argument something extending `Event`, which is not necessarily fully accurate and it does not scale to user-defined types.)

## Checklist

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, etc.)
* [x] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).