How to deal with spurious type script error: types of property $timeout of types base class and derived class are incompatible? - angularjs

Since upgrading to TypeScript 1.6, I've been getting what appears to be a spurious error with all my ng.Types:
Typescript 1.6 class 'derivedClass' cannot extend class 'baseClass':
types of property $timeout of types base class and derived class are
incompatible
Here's the code sample:
module app.Test {
class derivedClass extends baseClass {
// Notice there's nothing in the derived class
}
class baseClass {
constructor(timeout: ng.ITimeoutService, val1: string, otherVal: ng.ILogService) {
var vm = this;
vm.$timeout = timeout;
vm.someValue = val1;
vm.otherValue = otherVal;
}
$timeout: ng.ITimeoutService; // angular.ITimeoutService works
someValue: string;
otherValue: ng.ILogService;
}
}
angular.ITimeoutService works, or adding import ng= angular; in my modules also works. This didn't used to be an issue in the previous versions of Typescript.
Is it best practice to repeat the alias (eg: import ng = angular) in each module before using it, even though that's already done in the angular.d.ts?

Is it best practice to repeat the alias (eg: import ng = angular) in each module before using it, even though that's already done in the angular.d.ts
NO. The error is happening probably because you have done this.
The code you have posted works fine by default:

Related

After upgrading TypeScript, Angular controller registration now fails to compile

We were using TypeScript 2.2. After upgrading to 2.4, we now get this on compilation:
error TS2345: Argument of type 'typeof TopMenuController' is not assignable to parameter of type 'Injectable<IControllerConstructor>'.
Type 'typeof TopMenuController' is not assignable to type '(string | (new (...args: any[]) => IController) | ((...args: any[]) => void | IController))[]'.
Property 'push' is missing in type 'typeof TopMenuController'.
ts\controllers\TopMenuController.ts(2,18): error TS2559: Type 'TopMenuController' has no properties in common with type 'IController'.
I don't understand the first error and Googling it has been difficult. I'm only asking for assistance with the first error. (I'm getting the second error due to my attempts to resolve the first). Here's the controller:
export class TopMenuController implements angular.IController {
static $inject = ["$templateCache", "Restangular"];
constructor(
private readonly $templateCache: angular.ITemplateCacheService,
private readonly restangular: Restangular.IElement) {
}
}
And this is how it is registered.
angular.module("ngApp")
.config(Configuration.TemplateCacheConfigurator)
.controller("topMenuController", Controllers.TopMenuController)
How do i modify my controller definition or its registration so our code compiles again?
(Removing the implements angular.IController bit removes the second error, but the first remains)
Edit: I found this bug
Since all of the properties of IController are optional, I believe the errors you are seeing are a result of the new checking for "Weak Types" in TypeScript 2.4. Check this link from Microsoft for details. Also check this related Github issue.
Some relevant quotes from Microsoft:
In TypeScript 2.4, we’re adding a similar check for what we call weak
types. Any type that contains only optional properties is considered a
weak type since it provides few restrictions on what can be assigned
to it.
...
In TypeScript 2.4, it’s now an error to assign anything to a weak type
when there’s no overlap in properties.
...
You can think of this as TypeScript “toughening up” the weak
guarantees of these types to catch what would otherwise be silent
bugs.
Since this is a breaking change, you may need to know about the
workarounds which are the same as those for strict object literal
checks:
Declare the properties if they really do exist.
Add an index signature to the weak type (i.e. [propName: string]: {}).
Use a type assertion (i.e. opts as Options).
Edit: Based on this information, a simple solution would then be to implement one of the methods defined in IController. For example, as mentioned by #Amy in the comments, you could just define an empty $onInit method in your controller.
Edit: For the sake of completeness, here's the full code:
export class TopMenuController implements angular.IController {
static $inject = ["$templateCache", "Restangular"];
$onInit() { }
constructor(
private readonly $templateCache: angular.ITemplateCacheService,
private readonly restangular: Restangular.IElement) {
}
}
I also faced the same issue which I got resolved by
implementing IController
add this code before constructor (or anywhere in code this is my preference) $onInit = () => { };
here is the full code, Hope this will give a clear view
module MyApp {
export class HomeController implements angular.IController {
$onInit = () => { };
user: string;
constructor() {
this.user = "mali";
}
}
angular.module('app').controller('homeController', MyApp.HomeController)
}
Happy Coding
adding the following code fixed the issue
$onInit = () => { };

AngularJS TypeScript Service Error

I'm getting this error trying to add a service to a module. Can someone help point out what's wrong?
Angular 1.5.11 and TypeScript 2.2.2
ERROR in ./source/mainModule.ts
(185,33): error TS2345: Argument of type 'typeof AwesomeService ' is not
assignable to parameter of type 'Injectable<Function>'.
Type 'typeof AwesomeService ' is not assignable to type '(string | Function)[]'.
Property 'push' is missing in type 'typeof AwesomeService '.
Here is where I'm trying to add the service
export default angular.module('iris.service', [])
/* This line throws the error --> */.service('awesomeService', AwesomeService);
In a separate file, here is how I'm creating the service
export class AwesomeService extends OtherClass {
private static $inject = ['configService'];
constructor() {
super();
}
}
update:
I see that if I change AwesomeService to a function and export that, it works fine. Is there any way I can use a class for a Service? It looks like #types/angular specifies that the second argument to angular.module.service should be either a string or a function.
Yes, you can do exactly what you want, you simply need to write less code.
The type declaration in #types/angular includes the following
declare global {
interface Function {
$inject?: ReadonlyArray<string>;
}
}
This augments the declaration of the Function type.
Adding an optional property, $inject, to allow for clean, readable AngularJS DI annotations to be added to classes and functions in a type safe and declarative manner without the need for unsightly type assertions.
Note that all classes and functions are in fact functions.
The problem with your class is that, while the Function type augmentation above states that $inject is optional, it does not state that when it is specified, that it may be private.
In fact, it is very much not private, semantically, as it is read by the AngularJS framework and potentially other tools as well.
To resolve the issue simply write
export class AwesomeService extends OtherClass {
static $inject = ['configService']; // no private here.
constructor() {
super();
}
}
The more detailed answer is that two types are considered structurally incompatible if one declares a private member (to do this it needs to be a class) with the same name as a public member in the other (all interface members are public).
Unfortunately, the error is a bit cryptic. The private $inject declaration causes the type checker to immediately remove Function target types in the call to service. It then attempts to match Array and fails.

Why do I need to write static get parameters()

I been trying to migrate my Ionic 1 application to Ionic 2 and have been encountering alot of new terms and problems.
I do not really understand the need for get parameters() and when and what variables to return in that function.
For example(To Navigate):
static get parameters() {
return [[NavController]];
}
Previously in an older ionic build(I'm not sure which version I was developing upon), I can navigate to pages without this function. In the latest ionic 2 build(2.0.0-beta.25) , without this get parameters() function, it just wont navigate to the next targeted page , but there are no errors.
I'm aware of a existing post:
Angular/Ionic 2 - what is a provider and what does `static get parameters()` do?
Therefore, in what occasion I should return and what variables do I return?
In ES6 dependency injection gets the list of types it needs to resolve and pass instances for to the constructor of the class.
It has to be static, otherwise they could not be read before an instance is created.
In TypeScript it can aquire these types from the constructor parameters if they are applied (or #Inject() annotations).
Return the types for the constructor parameters of your class in the order they are listed in the constructor parameter list.
See https://stackoverflow.com/a/34546344/217408 for an example.
In fact, Angular2 tries to find out what to inject into the constructor of the class using the parameters method of the ReflectionCapabilities class:
https://github.com/angular/angular/blob/master/modules/angular2/src/core/reflection/reflection_capabilities.ts#L126
This method tries several things:
within the parameters of the type (i.e. a class property, i.e. static property)
#Component({
(...)
})
export class SomeComponent {
constructor(http) {
}
static get parameter() {
return [[Http]];
}
}
within the parameters metadata for the class if any. It's populated by #Inject decorator. Not that parameter decorators aren't supported in ES6
#Component({
(...)
})
export class SomeComponent {
constructor(#Inject(Http) http) { // == http:Http
}
}
within the design:paramtypes metadata that is internally created using the parameter types of the constructor. With ES6, this metadata isn't available since types for method parameters can't be used.
#Component({
(...)
})
export class SomeComponent {
constructor(http:Http) {
}
}
Based on these types, Angular2 looks for corresponding providers to get instances to inject.

CakePHP lazy loading fails with static access to class constants

In a CakePHP 2.2 app, I'm using class constants in a Model for some internal configuration. The following issue came up.
Short version:
Cake's lazy class loading will not be triggered by a static call to the Model class.
If the first access to a Model in a Controller is
MyModel::SOME_CONST // fails
the class will be unknown. If any instance of the class is used before, it's fine:
$this->MyModel->something();
MyModel::SOME_CONST // works
Not knowing about the details of the lazy loading implementation:
Question: Is this something that is impossible to fix? If so, why? How do I then best work around it in my App myself (wrap consts in a function)? Or is there a chance to improve the lazy loading so that it works with static access, too?
Long version with code:
In order to test the different cases, I made a small test App with 1 Model and 1 Controller:
Model/Post.php:
<?php
class Post extends AppModel {
public $useTable = false; // Don't bother with a DB
const FOO = "foo";
public $bar = "bar";
}
Controller/PostsController.php:
<?php
class PostsController extends AppController {
public function constant() {
debug(Post::FOO);
}
public function variable() {
debug($this->Post->bar);
}
public function variableFirst() {
debug($this->Post->bar);
debug(Post::FOO);
}
}
Accessing the three controller actions through the browser, the different cases can now be tested.
1) accessing the Model constant (at /posts/constant):
Error: Class 'AppModel' not found
2) accessing the Model variable (at /posts/variable):
'bar'
3) accessing the Model constant AFTER a variable (at /posts/variable):
'bar'
'foo'
lazyloading works with normal class calls as well as static calls IF you correctly approach it.
Correctly means, that you always have to App::uses() all used classes at the top of your file
for AppModel in a model file:
App::uses('AppModel', 'Model');
class Post extends AppModel {}
see the core files for details.

Codeigniter Undefined property: xxxx_model::$db only from Model

First the Model class:
class Xxxx_model extends Model
{
function XxxxModel()
{
parent::Model();
$this->load->database();
}
function isInDatabase()
{
// Please ignore the sql query, it's just to show some random sql code with results
11. $result = $this->db->query('SELECT * FROM someTable WHERE ...');
$numberOfRows = $result->num_rows();
...
return $test;
}
}
Now the controller:
function someLogic()
{
$this->load->model('xxxx_Model', 'xxxxModel'); // not necessary to specify
$this->xxxxModel->isInDatabase();
}
When I run this I get the error:
Severity: Notice --> Undefined property: Xxxx_model::$db .../xxxx_model.php line 11
I have no idea why this is. If I put the db code in the controller it seems to work, it's only with this setup in the model that it fails. I can't for the life of me figure out where the code is astray...
You have to load the db library first. In autoload.php add below code,
$autoload[‘libraries’] = array(‘database’);
add library 'datatabase' to autoload.
/application/config/autoload.php
$autoload['libraries'] = array(
'database'
);
Propably you're started new project, like me ;-)
To add to atno's answer:
class Xxxx_model extends Model
{
function XxxxModel() //<--- does not match model name Xxxx_model
{
parent::Model();
$this->load->database();
}
Basically, you are not constructing the class or the parent class Model. If you are on PHP5, you may use __construct(), otherwise you must match the class name exactly, regardless of what alias you load it with in your controller. Example:
class Xxxx_model extends Model
{
function __construct()
{
parent::__construct(); // construct the Model class
}
}
I may be mistaken (haven't used 1.x in a while), but if you construct the Model class, there's no need to load the database if you are using the default connection settings in config/database.php, it should already be loaded for you.
If function XxxxModel() isn't your constructor, you're not loading the database by calling $this->xxxxModel->isInDatabase();
Try autoloading the database library from within autoload.php, or create a proper constructor in your model.

Resources