Jest SpyOn choose the correct overload - reactjs

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));

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 does TypeScript allow overloading return type as 'any' when implementing interfaces

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.

EasyMock - CreateNiceMock

A fairly straightforward question regarding EasyMock. Read up a bunch of resources but not sure what I am missing:
The following snippet is creating a unit test using Test-ng:
#Test(groups = "unit")
public class SchoolTestEasyMock {
#Test
public void test1() {
School mockSchool = EasyMock.createNiceMock(School.class);
EasyMock.replay(mockSchool);
System.out.println(mockSchool.getSchoolNumber());
}
}
Let's assume the School class has a simple getter 'getSchoolNumber' that returns an Integer.
The snippet above is printing a 'null' to the console. Since I'm creating a 'nice' mock shouldn't the 'getSchoolNumber' return a default value of 0? Am I missing something while creating the nice mock?
From the documentation:
If you would like a "nice" Mock Object that by default allows all method calls and returns appropriate empty values (0, null or false), use niceMock() instead.
As Integer is an object, the default value is null.
If you change the return type of the method to int, the value will be 0 as expected.

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.

Resources