Repository: TypeScript
Issue: Ability to pick setter types (instead of getter types) in a mapped type
Labels: Suggestion,Awaiting More Feedback

### 🔍 Search Terms

typescript extract setter types mapped type

### ✅ 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.)
- [ ] 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

([original thread](https://discord.com/channels/508357248330760243/1292604210591825921) on Discord)

This might be a request for a new utility type, or a new language feature, because there seems to be no way to do it with current language features currently:



Given this type:

```ts
class Foo {
  get foo(): number {...}
  set foo(v: 'foo' | 'bar' | number) {...}
}
```


We want to derive a type like this:

```ts
type Derived = PickWithSetterTypes<Foo>
// result:
// {foo: 'foo' | 'bar' | number}
```


There seems to be no way to implement `PickWithSetterTypes` in userland.

### 📃 Motivating Example

When writing class-based JSX components (for example, custom elements are written as classes), the JSX property types are *setters, not getters*.

In this JSX expression:

```tsx
return <some-element foo={123} />
```

The `foo` prop is a `setter`, and it is setting the property on the custom element, it is *not reading* the property from the element.

So, when we define a class component, for example:

```ts
class SomeElement extends HTMLElement {
  get foo(): number {...}
  set foo(v: 'foo' | 'bar' | number) {...}

  someMethod() {... this method should not be included in JSX types ...}
}

customElements.define('some-element', SomeElement)
```

We need to pluck the properties that we want available in the JSX. For example, something along the lines of this:

```js
declare module 'some-lib' {
  namespace JSX {
    interface IntrinsicElements {
      'some-element': Pick<SomeElement, 'foo'>
    }
  }
}
```

Now, the problem is, when we try to set a valid value for the property in JSX, it will not work:

```tsx
return <some-element
  foo={'foo'} // Type Error: 'foo' is not assignable to number
/>
```

There should not be a type error, because the setter actually does accept the value `'foo'`.



### 💻 Use Cases

1. What do you want to use this for?
   - Any situations where the setter types need to be extracted, for example JSX props
3. What shortcomings exist with current approaches?
   - it is impossible right now
5. What workarounds are you using in the meantime?
   - Workarounds could include providing separate named properties that can be extracted with template string types, but it is very cumbersome
     ```ts
     class SomeElement extends HTMLElement {
       set foo(v: this['_set_foo']) {...}
       get foo(): number {...}
       
       /** do not use this property, it is for types only */
       _set_foo!: 'foo' | 'bar' | number
     }
     ```
     With this method, now a utility type can be written that can use template string types to extract the setter type from the non-setter dummy property type, something like this:
     ```ts
     type SomeElementJSXProps = JSXProps<SomeElement, 'fooBar' | 'foo'> // see linked playground below

     declare module 'react' {
         namespace JSX { interface IntrinsicElements { 'my-el': SomeElementJSXProps } }
     }
     ```
     [TypeScript playground example](https://www.typescriptlang.org/play/?target=99#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcA5FDvmQFA24A2a6AsgJ4CiDcWAHjFgB2AE3QAJACosAMlywgh8AN404auAG0ADAF0AhAC44ARgBMAZjgB6K3FQksPflGSCsEAK7oA1ljZwAd0dhCEEyeADXeGBBOBgAC0cAKQBlAA06dTgCCAgAIWQoQxMLa1t7BTgIBKwoODBiMFqYfyDAqLgYuMS4VLS4AApBCDsm3GBkbhhKZBgFQRgASkz1GzgJHr64tia7eM8GYTh45AA3Rxq7LBgBOpbdiAJu4HQGiCbYNlV1AHNr7NyA0WRkEHhAACNanAlJQYB4oLEtABfb5qVD-HIQAanIwJF4aMgAfXRMEJmLIOkW0JRqLgxOuZNyxTI5LgAB9yODCmR2XBQRDais1GsNsl0ttdqh9h5DsczhceiTbhLHI9nq9Gs0vlk-vAuVAgSCwZC6jDrvDESisiS4Prsbj4viiSTGRAKVSlDSsvTSfriuDcgwcLEOfyTTQaSLHegTuhLvcsOg2pEFgC6m8wOguls+A1E6hgKFUDQE3B2Fw+gAFRroAC8vXS1feqAAPOWGAAaciYgpQHkclm5fuc7kAPjowiwjEKjhAEGEMscFCoMB5KiygmQClQYDwYv666y6hitwIe7gAEkFlAYgXcHJ5jB0Iej0eyCA2ABaLAMMhGdtVjWtJZFa6gojSuBFvAABWqC8BIibwPWLYft+DC0pitZKIObpImUVTeLS+pYdMHhYHhawQIRtJrEIqDwo4MSJDeAhHFsGbNMAiZOC8MC0sAwhYQAROCTDxEJoFoi0QZYUokEMNARjLsIZBIiio4tlYqE-uO9BQXAsHwYhphwMh2noVkmFKGY5gUbYVFEYUWE2XZBE0BpWlfjpdCQYIqAwXBCH+ZYZleRZqy2AAAk+368GMMDflAxB1AGEBBq4nToMM8DIHApyTGReUFVgGG5CRUBkZJ+HRagsXxYlyV2NMMQ-JlfLVCgRUMIV+XdSVWTEUoQk2RJ7maeZulrKAYBBo+syFrENhLctK2rWt60bWtdCloBzYthIXaVsAuC+MIADSfjoHwAgiD4fhqtgc7nNWWAEMAvCJi2j0QOcACCuC4Pm0CthIo5dik1y3BIOxYC9b28KOo6mXAtIAPIgMAMBfSuAB0AAi1zIMAQbCJIMhNpm2N4DAONk9Iv03De4IeAIINg+s7NHSdWDnZd45ZAAZHAlaFDAEwMC2XPeNjT2w5Q8Ofd9f0A0DUBs+DkO1NDTRw+9nPHadF1sKgiPbTDcApM1gg-EbqCo4IDBsPtSP1gAqoIC0AOqY9KWMHXyxpQhyqBsBCaW6aW7tez7nh+122tYBIEBK1gLvrE4N2iOsMNJyncAAPx8lg5x1EYEgANxm7sKe6x9IOHfL70Z0IWf+Te1tp4eGhnZ0sS+GwaoSCg6A99dLfoG3LUF5g8g-XLr3vS2Z0NwvCNwEYZ06GX3c6JXNKljXje8Ptze3U17c-Cv8On63Vs-GnQ9j2fAAGAAkSi10i78xAQULYP5SJn7T3-vAMulcSzmxTv9QGqB7Bq2dsjF8mge5dH7oPYecBR78HHjPWWtcWyWwvrbe2jsCGaygAnW2AAxaAzt2YQxuFrGGtckaFzcCXdemCt7rB3hGKujgGFQxhtQ2hoNkZoKeLXKW+0NaMIocwo+EdzZSINjI4WR8b4Tzvp3b43de5wAkenNAmDNFwDfh-I+X8lCT2toA6ePcQTF1qNwiQO897gNLIIphOsNH1mdAyWgQA)