Early on in the development of Ionic 2, the team decided to fully embrace TypeScript over plain ES6 JavaScript. All Ionic 2 projects use TypeScript by default, and it’s quite difficult to modify the project to be rid of TypeScript. TypeScript is really just JavaScript with some added bonuses, though, so there are not really many good reasons to not want to use it.
TypeScript can be kind of like a tough parental figure or teacher. In the beginning, it may seem like it’s giving you a hard time for no reason, but eventually, you will come to appreciate that it’s just there to help you in the long run.
It can seem a little annoying in that it errors out and won’t let you run the application, but it’s just forcing you to fix potential errors upfront. I didn’t even realise at first, but since using TypeScript I rarely ever run into runtime errors anymore.
Generally, TypeScript errors are reasonably descriptive. However, if you are just coming across them for the first time they can seem a little cryptic. In this tutorial, I am going to walk through some common TypeScript errors you may encounter in an Ionic 2 application, and how you can fix them.
Missing Providers
Error in ./MyApp class MyApp - caused by: No provider for PROVIDER_NAME!
This error occurs when you attempt to use a provider, but have not added it to your app.module.ts file. You will need to import
it and add it to the providers
array:
import { NgModule, ErrorHandler } from '@angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { PROVIDER_NAME } from '../providers/provider-name';
@NgModule({
declarations: [MyApp, HomePage],
imports: [IonicModule.forRoot(MyApp)],
bootstrap: [IonicApp],
entryComponents: [MyApp, HomePage],
providers: [
{ provide: ErrorHandler, useClass: IonicErrorHandler },
PROVIDER_NAME,
],
})
export class AppModule {}
Accessing Objects on the Window
Various objects are available through the global window
object. In general, it would be better practice to avoid using this – you could use the Platform
service Ionic provides to get the height of a device rather than using window.innerHeight
for example – but sometimes you may need to access it.
If you were to attempt to access something on the window
object you may get the following error:
Property 'Something' does not exist on type 'Window'
Some libraries and plugins will add objects to the window
and TypeScript doesn’t have the correct types set up for this, so it thinks you are trying to incorrectly access something on the object (even though it already exists).
There are a couple of ways you can address this problem, including creating a custom interface, but I find the easiest way to circumvent the issue is to use square bracket notation instead of dot notation when accessing the object, e.g:
window['Something'];
instead of
window.Something;
Undefined Member Variables
Property 'some_property' does not exist on type 'HomePage'
This error can be caused by attempting to access a member variable (a variable on a class that is accessible through this.whatever
) that hasn’t been defined or hasn’t been defined properly. For example, you attempt to access:
ionViewDidLoad() {
console.log(this.person.name)
}
but you have not correctly set up person
as a member variable. Make sure that you define your member variable above the constructor, e.g:
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
person: any;
constructor(public navCtrl: NavController) {
this.person = {
name: 'Larry',
age: 25
};
}
}
You may also run into a similar error if you try to access a member variable that is not defined properly from a template:
Error in ./HomePage class HomePage caused by: Cannot read property 'name' of undefined
Again, you will need to make sure you set up the member variable correctly.
You will also need to make sure that you are actually setting up some data on that member variable before trying to access it. If you reference the variable in your template, but you only set the data on the object after running some function, it may not be available in time. If you have only declared the variable above your constructor and have not set any data on it initially it will cause an error.
To give you an example, if I have the following set up:
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { PersonService } from '../../providers/person-service';
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
person: any;
constructor(public navCtrl: NavController, public personService: PersonService) {
}
ionViewDidLoad(){
this.personService.getPerson().then((person) => {
this.person = person;
});
}
}
and I try to access person.name
in my template, it will cause an error because that data isn’t immediately available. An easy way to avoid this issue is to set up some intial blank data, e.g:
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { PersonService } from '../../providers/person-service';
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
person: any;
constructor(public navCtrl: NavController, public personService: PersonService) {
this.person = {
name: '',
age: ''
};
}
ionViewDidLoad(){
this.personService.getPerson().then((person) => {
this.person = person;
});
}
}
Now the template will still be using the correct interface initially, and once the data has loaded the template will automatically update.
Binding NgFor to non-Iterables
Error in ./HomePage class HomePage - caused by: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to iterables such as Arrays.
This error was caused by attempting to use an *ngFor
directive with an object:
<ion-list>
<ion-item *ngFor="let item of list"> hi </ion-item>
</ion-list>
The list
in this case is an object that is defined in the class for this template. You can not use objects with *ngFor
though, you should use an array instead.
Missing Types
Cannot find name 'something'
This error is very common when including some kind of third party library in your application. When implementing the Google Maps JavaScript SDK (which is very common), you need to reference the global google
object. If you attempt to do this, though, you will receive the following error:
Cannot find name 'google'
This is because TypeScript doesn’t know about the google
object, so it thinks that you are making a mistake. In order to suppress this error you will need to install the types
for whatever library you are using, which in general will look like this:
npm install @types/library-name --save
the name of the types package may vary and some libraries don’t supply types, so make sure to look it up first. If you are not able to get a types package working, you can also suppress the error by declaring the variable you are trying to access above the decorator, e.g:
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
declare var libraryName: any;
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
constructor(public navCtrl: NavController) {
}
ionViewDidLoad(){
}
}
With the code above I should be able to access libraryName
without causing any TypeScript errors. This is less preferred to installing the types for a library since we are essentially just circumventing TypeScript here.
Missing Component Factory
Error in ./MyApp class MyApp - caused by: No component factory found for HomePage
Any components that are added dynamically to the application need to be declared in the entryComponents
array in the app.module.ts file. In the case of Ionic 2, this generally means that all pages you create in Ionic 2 should be added to entryComponents
– not all components, just components that are used as pages.
If you run into this error, make sure that all of your pages are included in the entryComponents
array:
import { NgModule, ErrorHandler } from '@angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
@NgModule({
declarations: [MyApp, HomePage],
imports: [IonicModule.forRoot(MyApp)],
bootstrap: [IonicApp],
entryComponents: [MyApp, HomePage],
providers: [{ provide: ErrorHandler, useClass: IonicErrorHandler }],
})
export class AppModule {}
Component, Pipe, or Directive not Declared
'some-component' is not a known element:
1. If 'some-component' is an Angular component, then verify that it is part of this module.
The pipe 'some_pipe' could not be found
Components, pipes, and directives are all “declarables” and must be declared in the declarations
array of the app.module.ts file. If you do not import them and declare them in the module file then you will receive errors like the above, and your directives won’t work.
Make sure you add them to the declarations array like this:
import { NgModule, ErrorHandler } from '@angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { CoolComponent } from '../components/cool/cool';
import { CoolPipe } from '../pipes/cool-pipe';
import { CoolDirective } from '../components/cool-directive/cool-directive';
@NgModule({
declarations: [MyApp, HomePage, CoolComponent, CoolPipe, CoolDirective],
imports: [IonicModule.forRoot(MyApp)],
bootstrap: [IonicApp],
entryComponents: [MyApp, HomePage],
providers: [{ provide: ErrorHandler, useClass: IonicErrorHandler }],
})
export class AppModule {}
Incorrect File Path when Importing
Cannot find module "../../some/path"
When importing modules, the path that is supplied is relative to wherever the current file is:
import { NgModule, ErrorHandler } from '@angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { CoolComponent } from '../../components/cool/cool';
@NgModule({
declarations: [MyApp, HomePage, CoolComponent],
imports: [IonicModule.forRoot(MyApp)],
bootstrap: [IonicApp],
entryComponents: [MyApp, HomePage],
providers: [{ provide: ErrorHandler, useClass: IonicErrorHandler }],
})
export class AppModule {}
In this example, the app.component.ts
file is located in the same folder as this file, the home.ts
file is located one folder up and then inside the pages folder and then the home folder, and the cool.ts
file is located two folders up from the current folder, then inside the components folder, then inside the cool folder.
Assuming that the file actually exists, if you run into this error it is likely that you have supplied an incorrect path to the file.
Summary
As you get comfortable with TypeScript you will start breezing through these errors, but they can trip you up a bit at the start. Hopefully, this list covers most of the errors you will run into, but if I’ve missed something you think is important feel free to let me know in the comments.