Typescript inheritance in Angularjs 1.x - javascript

Am trying to make an abstract class service with methods and properties that other services can inherit n Angularjs. Using typescripts extend method would not work or i don't if am doing it properly, so tried using this pattern but sadly it too did not work. Typescript does not know the inherited methods from the prototypal inheritance. Is their any workaround or a solution to this, thank you for the help
example code for the Abstract service Class
'use strict';
namespace Data {
export interface IAbstractRepository {
sayHellow(): string;
}
class AbstractRepository implements IAbstractRepository{
static extend = function (repoCtor): void {
repoCtor.prototype = new AbstractRepository();
repoCtor.prototype.constructor = repoCtor;
};
sayHellow(): string {
return 'Hello world';
}
}
function factory(): IAbstractRepository {
return AbstractRepository;
}
angular.module('core').factory('AbstractRepository', factory);
}
and for the sub service class
'use strict';
namespace Data{
class BookRepository {
constructor(AbstractRepository) {
AbstractRepository.extend(this);
}
getAllBooks(): string {
// this shows as an error now it cant know that its inherited
return this.sayHellow();
}
}
factory.$inject = ['AbstractRepository'];
function factory(AbstractRepository): any {
return new BookRepository(AbstractRepository);
}
angular.module('core').factory('BookRepository', factory);
}
for the solution proposed down the flags for JshintRC to suppress warnings produced by Typescript
"validthis": true and "shadow": "false

Your question is not that clear, even with the comment you answered my question I'm still not sure what the problem is.
I'm not angular developer, so I can't answer angular specific questions, but as inheritance goes in typescript this is how you do it:
namespace Data {
export interface IAbstractRepository {
sayHellow(): string;
}
abstract class AbstractRepository implements IAbstractRepository {
constructor() {}
sayHellow(): string {
return 'Hello world';
}
}
class BookRepository extends AbstractRepository {
constructor() {
super();
}
getAllBooks(): string {
return this.sayHellow();
}
}
angular.module("core").factory("BookRepository", BookRepository);
}
If you'll tell me what's wrong with this, then I might be able to help you solve that.
Edit
Since the playground url is too long for the comments, here it is.

Related

Typescript - Extending class with type

Typescript newbie here.
Let's say I have a type coming from a library that looks like this:
type FooType {
name: string;
// Has much more attributes in real life
}
I now want to define a class called Foo like this:
import { FooType } from 'my-library';
class Foo {
constructor(data: FooType) {
Object.assign(this, data);
}
}
With this code, I'm able to define a foo instance, yet I have an issue with autocomplete:
const foo = new Foo({ name: 'foo name' });
// Typing in foo.name does not bring any type information about the "name" attribute
Is there a way I can make a class automatically "inherit" all attributes from a type without having to type them manually?
Edit after being marked as duplicate:
What I want to achieve is to avoid manually typing attributes that are already existing on a type.
Thanks to #Phil I've been provided an answer that mentions this as an ongoing issue within Typescript:
https://github.com/microsoft/TypeScript/issues/26792
What I will do for now is the following:
class Foo {
constructor(public _data: FooType)
Object.assign(this, _data);
}
get() {
return this._data;
}
}
const foo = new Foo({ name: 'Bar' });
foo.get().name; // After typing "foo.get()" the autocomplete works properly.
Note: this is just a workaround and doesn't fix the underlying issue with using Object.assign() in the constructor with TypeScript.
https://github.com/Microsoft/TypeScript/issues/16672
Update: this question definitely lead me down quite the rabbit and I wasn't quite happy with my previous response so here is something I think would work better for solving these two items:
Creating a class which extends an object
Allowing TypeScript to still do its thing
Step One: use the typescript mixin pattern to return an anonymous class with the properties of a generic object T:
function GenericBase<T>(data: T) {
return class {
constructor() {
Object.assign(this, data)
}
} as ({
new (...args: any[]): T;
})
}
Now we have an anonymous function with a constructor which is then casted as a class that returns our generic object T.
Step Two: Note that we don't need to touch the above code we just need to extend it, so for OP's example we would do this:
class MyClass extends GenericBase(foo) {
constructor() {
super()
}
greet() {
return `Hello, ${this.name}!`
}
}
More examples:
const myClass = new MyClass()
console.log(myClass.name) // bar
console.log(myClass.greet()) // Hello, bar!
function doSomethingWithFoo(someFoo: Foo) {
// example method that only takes types Foo
}
doSomethingWithFoo(myClass)
Try it on the TypeScript playground!

Testing of private methods in typescript

Let's say I have the following typescript class:
class MyComponent {
private something: number;
constructor () {
this.something = 0
this.incrementSomething()
}
private incrementSomething () : number {
return this.something++
}
}
export default MyComponent
And my goal is to test it with jest, but I've got more questions then answers.
Is this a bad design pattern?
Should private methods be tested? (there are many opinions on the net, hard to decide)
Should I ignore jest coverage with this setup as it will report class as untested?
Should I create a public method instead and call my private method within it?
It is my first attempt to use private methods in typescript and try to test them, so please be patient :)
I don't think SO is an ideal place for this kind of question, as most of what you're asking is opinion based. You can however, assert that the object is any in order to do some testing:
class MyComponent {
private something: number;
constructor () {
this.something = 0
this.incrementSomething()
}
private incrementSomething () : number {
return this.something++
}
}
const thingIWantToTest = new MyComponent();
console.log((thingIWantToTest as any).something); // works
console.log(thingIWantToTest.something); // type error

How to define method on 3rd party class using typescript?

I'm trying to extend a 3rd party class but am having trouble getting typescript to play nice. Basically, I can't use any existing method already defined in the class in my new method.
A workaround would be to redefine existing methods in extensions.ts (see below), but there just has to be a better way.
3rd party index.d.ts
export as namespace thirdParty;
export Class SomeClass {
// some methods here
}
My extensions.ts
import {thirdParty} from 'thirdParty'
declare module 'thirdParty' {
namespace thirdParty {
class SomeClass{
newMethod(): this
// works if I redfine the method here
originalExistingMethod(): number
}
}
}
thirdParty.SomeClass.prototype.newMethod = function() {
return this.originalExistingMethod() + 1
}
When calling an existing method like this.originalExistingMethod() above, typescript complains:
TS2339: Property 'originalExistingMethod' does not exist on type 'SomeClass'
Is there a way to avoid having to redefine existing methods when performing module augmentation?
Initial Answer
Here is an example using the Tensorflow library.
extend.ts
import { AdadeltaOptimizer } from '#tensorflow/tfjs-core';
declare module '#tensorflow/tfjs-core' {
interface AdadeltaOptimizer {
newMethod(message: string): void;
}
}
AdadeltaOptimizer.prototype.newMethod = function (message: string) {
console.log('===============');
console.log(message);
console.log('===============');
}
index.ts
import { AdadeltaOptimizer } from '#tensorflow/tfjs';
import "./extend";
const optimizer = new AdadeltaOptimizer(10, 10);
// the existing method is present
const className = optimizer.getClassName();
// the augmentation is also present
optimizer.newMethod(`The className is ${className}.`);
There is a similar example in the official TypeScript documentation, which augments Observable with a map method.
Follow Up on Comments
Thanks. Though my issue is using existing methods when defining newMethod. So in extend.ts not in index.ts. Any ideas on this?
This also works in extend.ts as follows:
import { AdadeltaOptimizer } from '#tensorflow/tfjs-core';
declare module '#tensorflow/tfjs-core' {
interface AdadeltaOptimizer {
newMethod(message: string): void;
}
}
AdadeltaOptimizer.prototype.newMethod = function (message: string) {
// just access the original method on `this`
const className = this.getClassName();
console.log('===============');
console.log(className);
console.log(message);
console.log('===============');
}

Parse.com with TypeScript: Extend Parse.Object

Disclaimer: I know, Parse.com shuts down it's hosted service. Still, we will continue to use the framework for a while, so this question is still important to us.
Recently, I started playing around with TypeScript and figured it might enhance my productivity for parse cloud code a lot. So I did some testing and was successfully able to use typescript to write cloud functions and so on. I even included the typing definition for parse via typings.
However, I still don't get one thing: How can I extend Parse.Object in a type-safe manner?
In normal js I would write:
var Foo = Parse.Object.extend("Foo", {
// instance methods
}, {
// static members
});
In order to get type safety, I would like to write something like this in typescript:
class Foo extends Parse.Object {
// static members
// instance methods
}
Is something like this possible? Am I missing out on something?
Yes, this is possible. There are a few steps necessary for this. First, you need to pass the classname to the parent constructor
class Foo extends Parse.Object {
// static members
// instance methods
constructor() {
// Pass the ClassName to the Parse.Object constructor
super('Foo');
}
}
Furthermore, you need to register your class as an Parse object:
Parse.Object.registerSubclass('Foo', Foo);
After this, you can just use it for your query as:
var query = new Parse.Query(Foo);
query.find({
success: obj: Foo[] => {
// handle success case
},
error: error => {
// handle error
}
});
This also works for the new parse server open source project: https://github.com/ParsePlatform/parse-server
If someone like my stumble across the same question, here are my findings so far:
import Parse from "parse";
interface IBase {
readonly createdAt?: Date;
readonly updatedAt?: Date;
}
interface IMyInterface extends IBase {
name: string;
age?: number;
}
class MyClass extends Parse.Object<IMyInterface> {
constructor(attributes: IMyInterface) {
super("MyClass", attributes);
}
}
export default MyClass;
You can then use it like this:
const newObject = new MyClass({ name: theName, age: theAge });
const result = await newObject.save();
const { attributes } = result;
console.log("Save result", attributes);
You will have full TS support for the attributes then.
Hopefully the work on https://www.npmjs.com/package/#parse/react will proceed quickly.

Implementing prototypes for interfaces in TypeScript

I have created a TypeScript interface for my service results. Now I want to define a basic functionality for both my functions inside. The problem is I get an error:
The property 'ServiceResult' does not exist on value of type 'Support'.
I use WebStorm for development (VS2012 makes me nervous because on freezes by large projects - waiting for better integration:P).
Here's how I do it:
module Support {
export interface ServiceResult extends Object {
Error?: ServiceError;
Check?(): void;
GetErrorMessage?(): string;
}
}
Support.ServiceResult.prototype.Check = () => {
// (...)
};
Support.ServiceResult.prototype.GetErrorMessage = () => {
// (...)
};
I have also tried to move my prototypes into the module, but same error still... (of course I removed Support. prefix).
You can't prototype an interface because the compiled JavaScript does not emit anything related to the interface at all. The interface exists purely for compile-time use. Take a look at this:
This TypeScript:
interface IFoo {
getName();
}
class Foo implements IFoo {
getName() {
alert('foo!');
}
}
Compiles to this JavaScript:
var Foo = (function () {
function Foo() { }
Foo.prototype.getName = function () {
alert('foo!');
};
return Foo;
})();
There is no IFoo in the result, at all - which is why you are getting that error. Typically you wouldn't prototype an interface, you would prototype a class that implements your interface.
You don't even have to write the prototype yourself, just implementing the interface as a class is enough and the TypeScript compiler will add the prototype for you.
It looks like you are trying to add implementation to an interface - which isn't possible.
You can only add to a real implementation, for example a class. You may also decide to just add the implementation to the class definition rather than directly using prototype.
module Support {
export interface ServiceResult extends Object {
Error?: ServiceError;
Check?(): void;
GetErrorMessage?(): string;
}
export class ImplementationHere implements ServiceResult {
Check() {
}
GetErrorMessage() {
return '';
}
}
}
Support.ImplementationHere.prototype.Check = () => {
// (...)
};
Support.ImplementationHere.prototype.GetErrorMessage = () => {
// (...)
};

Categories