I have a decorator that define property a class with a getter that return an Observable
and setter that emit new value on subject.
import { Subject } from "rxjs";
export const defineDecorator = () => {
return (target: object, propertyKey: string) => {
Object.defineProperty(target, propertyKey, {
const sub = new Subject();
const obs = sub.asObservable();
get: (): any => {
return obs;
},
set: (value: any) => {
sub.next(value);
},
enumerable: true,
configurable: true,
});
}
};
I want specify in typescript, "value" propery is Observable<string>
on "get" and string
on "set":
export class MyComponent {
@defineDecorator()
value!: any; // instead of any.. what type?
}
what type for "value"?
CodePudding user response:
This pattern is not currently supported in TypeScript (as of version 4.9.3
at least). Here's a simplified example which demonstrates the issue:
interface Foo {
get bar (): { value: string }; /*
~~~
The return type of a 'get' accessor must be assignable to its 'set' accessor type(2380) */
set bar (value: string);
}
Similar to the example above — in the code you've shown, the return type of the getter would be Observable<string>
, which is not assignable to the setter's parameter type string
.
You can read more context about this pattern and behavior at the following GitHub links:
microsoft/TypeScript #2521 - Suggestion: allow get/set accessors to be of different types
microsoft/TypeScript #43662 - Allow setter type to be incompatible with the getter type
CodePudding user response:
Per the previous answer, it's not possible but you can do it differently. What if instead of plain property you return a function that will have overload signature and depending on how you can it will act as a setter or getter
export const defineDecorator = () => (target: object, propertyKey: string) => {
const sub = new Subject();
const obs = sub.asObservable();
Object.defineProperty(target, propertyKey, {
enumerable: true,
configurable: true,
value: (param: any) => {
if (param !== undefined) {
sub.next(param);
} else {
return obs;
}
},
});
};
interface Value<T> {
(): Observable<T>;
(s: T): void;
}
export class MyComponent {
@defineDecorator()
value!: Value<string>;
constructor() {
// if used without param returns observable
const shouldBeObservable = this.value();
// if param is passed used as setter
this.value('some string here');
}
}