9

I'm trying to implement in my LoginService a isLoggedIn boolean value of type BehaviorSubject together with getter and setter functions to get the value as an Observable / set the variable correctly via its BehaviorSubject. This is working, however it throws two errors in TSLint about "Type not assignable" and "Dublicate identifier". What would be the right way to define it without TSLint complaining about.

This is a stripped down version of the above mentioned code:

@Injectable()
export class LoginService {
  public isLoggedInSource = new BehaviorSubject<boolean>(false);
  public isLoggedIn: Observable<boolean> = this.isLoggedInSource.asObservable(); // Duplicate identifier 'isLoggedIn'.

  constructor(private http: Http) {}

  set isLoggedIn(logged): void { // Duplicate identifier 'isLoggedIn'.
    this.isLoggedInSource.next(logged);
  }

  get isLoggedIn(): Observable<boolean> { // Duplicate identifier 'isLoggedIn'.
    return this.isLoggedInSource.asObservable();
  }

  logout() {
    this.isLoggedIn = false; // Type 'boolean' is not assignable to type 'Observable<boolean>'.
  }

  login(body) {
    return this.http.post('/login', body)
        .map(res => {
                if (res.token) {
                  this.isLoggedIn = true; // Type 'boolean' is not assignable to type 'Observable<boolean>'.
                }
                return res;
              })
        .catch(err => Observable.throw(err););
  }
}
Marcello di Simone
  • 1,612
  • 1
  • 14
  • 29
  • 2
    use different names for the property and getter/setters. – toskv Oct 07 '16 at 10:20
  • also... there isn't really a point to having a getter/setter for a public property. The users of your class would be able to go around them anyway. – toskv Oct 07 '16 at 10:22

1 Answers1

7

When you use TypeScript getter/setter, you have to rename your property, so the property name should be different from getters/setters name.

Moreover, you can modify your code by setting your behaviorSubject as a private member of your service, and just expose your Observable.

@Injectable()
export class LoginService {

  private isLoggedInSource = new BehaviorSubject<boolean>(false);

  public _isLoggedIn: Observable<boolean> = this.isLoggedInSource.asObservable();

  constructor() {}

  set isLoggedIn(logged: boolean) {
    this.isLoggedInSource.next(logged);
  }

  get isLoggedIn() {
    return this._isLoggedIn;
  }

  logout() {
    this.isLoggedIn = false;
  }

  login() {
    this.isLoggedIn = true;
  }

} 

And you will be able to listen change in your component :

export class App {
  constructor(private loginService: LoginService) {

    loginService.isLoggedIn.subscribe(bool => console.log(bool));

    //Wait and simulate a login
    setTimeout(() => {
      loginService.login();
    }, 1200);

  }
}
Paul Boutes
  • 3,285
  • 2
  • 19
  • 22
  • How would you solve error handling using this pattern? – martinoss Jun 08 '17 at 12:32
  • 1
    You can handle your errors on the service layer. But you cannot send something like `new Error()` to the subject stream, because it will kill it. You should split your architecture into two parts : a **data generation flow** and a **data store**. You can refer to this [post](https://stackoverflow.com/questions/41827371/how-do-i-throw-an-error-on-a-behaviour-subject-and-continue-the-stream) – Paul Boutes Jun 08 '17 at 13:08
  • 10
    I'm getting an `error TS2380: 'get' and 'set' accessor must have the same type.` – DeanB_Develop Apr 19 '18 at 15:52
  • Thats correct, getter and setter should have the same type. So thats not an option. – liqSTAR Jul 30 '19 at 07:06
  • The above code won't work. You can't return an observable in the getter and have a boolean value as input in the setter for the same property. – Lorraine R. Jan 30 '22 at 21:42