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.
Related
I do have a class that has 2 overloaded methods.
public static create<M extends Model>(
this: ModelStatic<M>,
values?: M['_creationAttributes'],
options?: CreateOptions<M['_attributes']>
): Promise<M>;
public static create<M extends Model>(
this: ModelStatic<M>,
values: M['_creationAttributes'],
options: CreateOptions<M['_attributes']> & { returning: false }
): Promise<void>;
in my unit test, I'm trying to use jest.spyOn to mock the first method however jest sees only the one that returns Promise<void>.
const mockInsightCreate = jest.spyOn(Insight, "create");
mockInsightCreate.mockReturnValue(Promise.resolve()); // here I need to return an object of type - Insight
Is there a way to instruct spyOn to pickup the first method that returns Promise<M> ?
import {
Model,
} from "sequelize-typescript";
...
export default class Insight extends Model<Insight> {
I know this question is old, but bellow is my answer for anyone facing the same issue who might stumble across it.
There is currently no way of instructing which overload signature spyOn should use. One solution would be to cast the value to whatever spyOn expects.
mySpy.mockReturnValue(Promise.resolve(myObj) as unknown as void);
mySpy.mockResolveValue(myObj as unknown as void);
However, I personally prefer avoiding coercing the typing system if possible. Another workaround that does not require any typing judo is to mock the entire method implementation.
mySpy.mockImplementation(() => Promise.resolve(myObj));
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 = () => { };
I am trying to determine why TypeScript allows you to overload the return type of a function to the type 'any' from a more specific type when implementing an interface.
In my case I am working in Angular and am injecting the implemented class.
My environment:
Visual Studio 2017
Angular Version 1.5.5
TypeScript Version 2.1.5
The following code compiles without any issue:
export interface IFoo {
thing: (parameter: number) => string;
}
export class BarService implements IFoo {
public thing = (parameter: number): any => {
return { "whatever": parameter };
}
}
angular.module("FooBar").service("barService", BarService);
So now when I attempt to consume the IFoo interface and am expecting a string to be returned from the 'thing' function call the compiler actually allows it to happen!
export class Whatever {
public foo: IFoo;
public myString: string;
static $inject = ["barService"];
constructor(barService: IFoo) {
this.foo = barService;
this.myString = this.foo.thing(0);
}
}
It seems that TypeScript should fail to compile when the return type is overloaded with type 'any' because consumers of the interface are expecting a strongly typed object.
Here is what I have put together.
From https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#3.1:
All types in TypeScript are subtypes of a single top type called the
Any type. The any keyword references this type. The Any type is the
one type that can represent any JavaScript value with no constraints.
And also:
The Any type is used to represent any JavaScript value. A value of the
Any type supports the same operations as a value in JavaScript and
minimal static type checking is performed for operations on Any
values. Specifically, properties of any name can be accessed through
an Any value and Any values can be called as functions or constructors
with any argument list.
Playing around:
interface IPerson {
name: string
}
class Person implements IPerson {
name: any;
}
// Error
//class Person2 implements IPerson {
// name: number;
//}
const person: Person = new Person();
person.name = 3;
let x: number = 3;
x = <any>"hello"; // Works!
//x = "hello"; // Error
We can see in even a simple example above that any can be used to override the type system which follows the docs.
My belief is that this behavior is there to allow the flexibility of javascript's untyped (flexible) behavior.
I'm not an expert on Typescript but based on my understanding of any:
https://www.typescriptlang.org/docs/handbook/basic-types.html
We find this comment on that page:
"We want to opt-out of type-checking and let the values pass through compile-time checks."
So I'm guessing when the compilers sees any, it says "I will not confirm this type". Ergo this condition must be sufficient for the compiler to assume that the function has been implemented correctly.
I used to think of any as "accept anything" (which is does). But more precisely it means "Assume this is the type you want.", from what I've seen. So in this case the compiler assumes it is string for your convenience and allows the compile-time check to pass.
As i see it there are two places that you could expect TypeScript compiler to fail. BarService returning any and Whatever class assigning return value from thing() function to myString.
The compiler doesn't fail when you return any in your BarService because in TypeScript you can use any as a substitude for any/all Types. The type any in TypeScript is mainly thought of to make it easier to support old javascript libraries or code. Check the Any section here: https://www.typescriptlang.org/docs/handbook/basic-types.html. And yes can and is abused.
The compiler doesn't fail in your Whatever class because here you are using the interface that in fact does says that the thing() function returns a string. The compiler do not know about the BarService when compiling the Whatever class.
I have the following little class in TypeScript, with some public fields decorated:
class Company {
#dataMember
public name: string;
#dataMember
public people: Person[];
}
class Person {
// ...
}
By using reflect metadata, I can determine the types of Company properties name and people: they are the constructor functions String and Array, respectively, which is expected and logical.
My property decorator function:
function decorate(target: Object, propertyKey: string | symbol): void {
var reflectType = Reflect.getMetadata("design:type", target, propertyKey);
// ...
}
But how could I determine the type (constructor function) of array elements? Is it even possible? In the above example, it should be (a reference to) Person.
Note: I need the type reference before instantiation, and because of this, it is impossible to dynamically determine the type using array items: there are no array items, there isn't even an Array instance.
I don't think this is possible as of now. If you see the generated js file, for array of anything, it creates metadata with type as Array without any information on type.
__decorate([
dataMember_1.dataMember,
__metadata('design:type', Array)
], Company.prototype, "people", void 0);
For built-in types, one way I could think of solving this problem is to pass the type in the decorator itself and writing the custom logic in the decorator code.
#dataMember(String)
myProp: Array<String>
For Custom objects, most of the time when the decorator call is fired, the module is not fully loaded. So, one way is to pass the class name and parse it later.
#dataMember("People")
people: People[]
I'm compiling three.d.ts (available from here) with the TypeScript develop branch. I get the following error:
Types of static property 'Utils' of class 'THREE.Shape' and class 'THREE.Path'
are incompatible
The problem is that
Shape defines a static Utils class
Shape indirectly inherits from Curve
Curve also defines a static Utils class with a signature unrelated to Shape.Utils
which is ill-formed according to the language spec. Summarised, three.d.ts contains something like the following code:
declare class A {
static Utils: {
f (): any;
}
}
declare class B extends A {
static Utils: {
// incompatible with A.Utils, without f(): any
g (): any;
}
}
Putting aside the question of why the type of a static member must be compatible with that of an inherited static member of the same name - which isn't the case in several other OO languages, but which does appear to be the case in TypeScript - I would like to know how to fix three.d.ts so I can compile it.
My current workaround is simply to copy and paste the signature of Curve.Utils into Shape.Utils, so that the latter structurally extends the former. But what is the "right" way to capture the signature of the underlying three.js file (here) in a .d.ts file? Is this a case of inheritance being used incorrectly?
Short answer is, per spec, Typescript doesn't allow member hiding through inheritance, like C# does automatically for example.
As defined in the language specifications section 8.2.3
A derived class inherits all members from its base class it doesn’t override. Inheritance means that a
derived class implicitly contains all non-overridden members of the base class. Both public and private
members are inherited, but only public members can be overridden. A member in a derived class is said
to override a member in a base class when the derived class member has the same name and kind
(instance or static) as the base class member. The type of an overriding member must be a subtype
(section 3.8.2) of the type of the overridden member, or otherwise a compile-time error occurs.
and
Base class static members can be overridden by derived class static members of any kind as >long as the
types are compatible, as described above.
Maybe they added some type checking into the latest compiler version which was missing before...
Here is a proposed solution, based on inheritance:
declare class UtilsA{
f():any;
}
declare class UtilsB extends UtilsA{
g():any;
}
declare class A {
static Utils:UtilsA;
}
declare class B extends A {
static Utils:UtilsB;
}