I'm using Angular with @ngneat/query
and RxJS to handle API requests. In my code, I throw an object containing an errorCode
, but TypeScript complains that errorCode
does not exist on t.error
.
I have the following HTML:
<div *ngIf="todos | async as todos">
<div *ngIf="todos.error">error code: {{ todos.error.errorCode }}</div>
</div>
And in my TypeScript code:
this.todos.subscribe((t) => {
const errorCode = t.error?.errorCode; // TypeScript error here
});
Error:
NG9: Property 'errorCode' does not exist on type 'Error'.
Code Example
Here’s the full reproducible example:
StackBlitz
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { of, tap } from 'rxjs';
import { injectQuery } from '@ngneat/query';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-root',
imports: [CommonModule],
template: `
<div *ngIf="todos | async as todos">
<div *ngIf="todos.error">error code: {{ todos.error.errorCode }}</div>
</div>
`,
})
export class App {
#query = injectQuery();
todos = this.#query({
queryKey: ['todos'],
queryFn: () =>
of({ data: { status: 500 } }).pipe(
tap((response) => {
if (response.data.status === 500) {
throw { errorCode: response.data.status };
}
})
),
}).result$;
ngOnInit() {
this.todos.subscribe((t) => {
const errorCode = t.error?.errorCode; // TypeScript error here
});
}
}
bootstrapApplication(App);
What I've Tried
- I verified that the error is indeed an object (
{ errorCode: number }
) when thrown. - TypeScript seems to assume
t.error
is of typeError
, which doesn't haveerrorCode
.
Why is TypeScript treating t.error
as Error
, and how can I correctly type it so that errorCode
is recognized?
I'm using Angular with @ngneat/query
and RxJS to handle API requests. In my code, I throw an object containing an errorCode
, but TypeScript complains that errorCode
does not exist on t.error
.
I have the following HTML:
<div *ngIf="todos | async as todos">
<div *ngIf="todos.error">error code: {{ todos.error.errorCode }}</div>
</div>
And in my TypeScript code:
this.todos.subscribe((t) => {
const errorCode = t.error?.errorCode; // TypeScript error here
});
Error:
NG9: Property 'errorCode' does not exist on type 'Error'.
Code Example
Here’s the full reproducible example:
StackBlitz
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { of, tap } from 'rxjs';
import { injectQuery } from '@ngneat/query';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-root',
imports: [CommonModule],
template: `
<div *ngIf="todos | async as todos">
<div *ngIf="todos.error">error code: {{ todos.error.errorCode }}</div>
</div>
`,
})
export class App {
#query = injectQuery();
todos = this.#query({
queryKey: ['todos'],
queryFn: () =>
of({ data: { status: 500 } }).pipe(
tap((response) => {
if (response.data.status === 500) {
throw { errorCode: response.data.status };
}
})
),
}).result$;
ngOnInit() {
this.todos.subscribe((t) => {
const errorCode = t.error?.errorCode; // TypeScript error here
});
}
}
bootstrapApplication(App);
What I've Tried
- I verified that the error is indeed an object (
{ errorCode: number }
) when thrown. - TypeScript seems to assume
t.error
is of typeError
, which doesn't haveerrorCode
.
Why is TypeScript treating t.error
as Error
, and how can I correctly type it so that errorCode
is recognized?
1 Answer
Reset to default 1First, don't throw non-Errors. Yes, it's possible. But it violates expectations and it will blow up on you at some unspecified time in the future. Instead, make a custom error class:
class MyError extends Error {
constructor(message: string, public errorCode: number) {
super(message);
}
}
I've done this for HTTP errors (so I can attach the HTTP response status code) in basically every TS codebase I've ever worked in.
But since literally anything can be thrown
(seriously, try throw undefined
sometime and watch all hell break loose), you will need to convince (both the compiler and yourself) that a caught value is of the appropriate type:
} catch(err: unknown) {
if (err instanceof MyError) {
console.log(err.errorCode); // no problem
}
}
Alternatively for more flexibility you can use a type predicate instead of the instanceof
check:
function isMyError(x: unknown): x is MyError { // note the 'is'
return Boolean(
x
&& typeof x === 'object'
&& 'errorCode' in x
&& typeof x.errorCode === 'number'
&& 'message' in x
&& typeof x.message === 'string'
);
}
Playground