Ability to get setter types (because now they can be different than getter types).
⭐ Suggestion
Now that TS 4.3 introduces the awesome ability to have setter types be a different type than getters, it would be useful to be able to get the type of a setter or getter (not just the property as a whole, which defaults to the type of the getter).
🔍 Search Terms
typescript 4.3 get type of setter
✅ Viability 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, new syntax sugar for JS, etc.)
- [x] This feature would agree with the rest of TypeScript's Design Goals.
📃 Motivating Example
Basically this example shows the issue:
class Foo {
foo = 123
_bar: any
get bar(): number { return this._bar }
set bar(v: number | string | boolean) { this._bar = Number(v) }
baz = 'baz'
set(props: Partial<this>) {
Object.assign(this, props)
return this
}
}
const f = new Foo
const props = {
foo: 2,
bar: '123',
baz: 'blah'
}
f.set(props) // Type error.
console.log(f.foo, f.bar, f.baz) // but it works great at runtime
💻 Use Cases
It is currently possible to achieve the desired effect by manually setting the types of the props object, but this is more cumbersome, especially when there are many types. I.e., the burden is on the end user instead of the class author (class author can serve many, instead of end users all having to write type defs).
Here's an example of that. The end user of the class as any as number, which is not ideal.
Ideally the class author would be able to write the set method with setter types, something like:
set(props: Partial<setters this>) {
Object.assign(this, props)
return this
}
I don't know what the syntax would be, but the idea is that the result of setters this is a type that has the types of the setters for properties that are originally accessors. The other properties works the same as usual.
Or maybe we need types like get this['foo'] and set this['foo'] that can return the getter or setter type of a specific property, and then one can create a mapped type that can use the get or set type operators.
Or, in other words, based on structural typing, { foo: number, bar: string | number | boolean, baz: string } is technically assignable to { foo: number, bar: string, baz: string }.
Maybe the assignability test needs to look at setter types under the hood? This would make it work without adding a syntax addition.
It seems that really we only care about the getter type when we're getting the value.
So the assignability check would need to use setter types of the destination object, or getter types of the source object.
F.e. in Object.assign(destination, source) or destination = source, the type of source should use getter types, while the types of destination should use setter types.
To add my use case, I would like consumers of my class to be able to assign a setter with a value T or a function that returns a value T, and the on the getter return T (evaluate the function as needed).
The motivation is to:
- provide more wrist friendly assignments when the function syntax isn't needed.
- provide a more intuitive result from the getter than () => T
- setup chains of dependent properties for lazy evaluation and/or revaluation on each evaluation e.g.
_baz = () => this.bar
class Foo {
constructor(spec?: Partial<Foo>) {
Object.assign(this, spec);
}
_bar : string | (() => string) = ''
public get bar(): string {
if (typeof this._bar === 'function') {
return this._bar();
}
return this._bar;
}
public set bar(value: string | (() => string)) {
this._bar = value;
}
}
//Using assignment with string is fine
let foo = new Foo();
foo.bar = "Hello";
console.log(`string assigned setter evaluates to${foo.bar}`)
//Using assignment with function is fine
foo.bar = () => "Hello";
console.log(`function assigned setter evaluates as ${foo.bar}`)
//Using Partial<T> with value as string is fine
let foo2 = new Foo({bar: "Hello"});
console.log(`partial assignment of setter to string results in ${foo2.bar}`)
//Using Partial<T> with () => string gives a type error
let foo3 = new Foo({bar: () => "Hello"} ); //compiler complains unless I force type with 'as any'.
console.log(`Partial assignment if setter with function results in ${foo3.bar}`)
Another use case: In my web component library, TypeScript emits a .d.ts file for the component class - the class may contain getters and setters of different types.
Based on this component class type, I wish to define the typings for my library for React JSX, Preact JSX and other JSX libraries.
However, when I use TypeScript's mapped type syntax, or Pick<>/Omit<>, I am only able to retrieve the getter types.
For JSX typing, I care only about the setter types.
Closing as duplicate of
- https://github.com/microsoft/TypeScript/issues/60162