class Thing<T> extends T - javascript

Regarding the title, is something equivalent to class Thing<T> extends T possible in typescript? Preferably in a way that's readable. Currently trying this line of code results in the error Cannot find name 'T' in VSCode. Tried finding an answer on here and google, but found nothing similar.

You can't do it directly, you can crete an intermediary class and then use a type assertion to get it to fit with a custom constructor signature that returns T and the class:
class _Thing { }
const Thing: {
new <T>(): _Thing & T
} = _Thing as any;
new Thing<{ prop: string }>().prop

T is a type, only values (classes) can be extended:
function <T extends new(...arg:any[]) => any> Mixin(Parent: T) {
return class Mixin extends Parent {
// ...
}
}
const Sub = Mixin(class Parent {
works() { /*..*/ }
//...
});
(new Sub).works();

Related

How to implement abstract class with abstract method in Angular

I have a perhaps trivial question, but I cannot understand how I can use the methods of an abstract class based only on calling it and choosing the correct override based on the type of value I have as Input.
For example, if I have something like this:
export abstract class Parent {
constructor(
public type: View
){}
abstract getSource(): string | string[]
}
export class Child1 extends Parent {
getSource(): string {
return this.type.view1
}
}
export class Child2 extends Parent {
getSource(): string[] {
return this.type.view2
}
}
And I wanted to call the abstract class Parent, and, based on the type, figure out whether to use the function in the Child1 or Child2 class. I thought you could do it like this:
export class AngularComponent {
public _VieweSource: string | string[];
#Input() set data(type: View) {
// here i need to return Child1.getSource() or Child2.getSource()
// not calling new Child1(type).getSource()
// but something like this: Parent(type).getSource() --> Child1 if type.view1=string || Child2 if type.view2=string[]
}
}
the View type are this:
{type: "simple", view1: "hello world"} || {type: "complex", view2:["hello", "world"]}
Sorry if the question is wrong, if there are any questions I can answer.
Thanks in advance.
The problem is that you are trying to choose which class to use, based on the values in type. You should not try to let inheritance figure out which class to choose, a simple if statement is build for that.
export class AngularComponent {
public _VieweSource: string | string[];
#Input() set data(type: View) {
let obj: Parent;
if (type.view1 && typeof type.view1 === 'string'){
obj = new Child1(type);
} else if(type.view2 && type.view2 instanceof Array){
obj = new Child2(type);
}
obj.getSource();
}
}

TypeScript array of generic classes

I'm having trouble getting the TypeScript compiler to not give me grief. I have a controller which has an array of classes, NOT the instance of the class, but the class itself. All of these will extend off of the same base, UnitView. For some reason I get an error if my TitleView does not also accept generics, but I get no errors if it does.
I'm not understanding why TitleView would need to accept generics because it passes the Model type explicitly to the UnitView. Can anyone explain or see anything that I'm doing wrong?
Code:
class Model { }
class View<TModel extends Model> {
private model: TModel;
}
class UnitView<TModel extends Model> extends View<TModel> { }
class TitleView extends UnitView<Model> { }
class Controller {
private ViewClass: typeof UnitView;
private ViewClasses: typeof UnitView[] = [
TitleView
]
}
And here is a direct link to TypeScript playground if you want to test it there
The error has nothing to do with arrays. Here is a minimal reproduction of your bug:
class View<TModel> {
model: TModel;
}
class UnitView<TModel> extends View<TModel> { }
class TitleView extends UnitView<{}> { }
const example1: typeof UnitView = TitleView; // ERROR
TileView is not assignable to typeof UnitView. The key reason being that the type of T (a generic) are not compatible with {}.
This is similar to the further simplified example shown below:
class Foo<T> {
model: T
}
class Bar{
model: {}
}
const example2: typeof Foo = Bar; // ERROR
TLDR
Generics T and instances (even {}) are not compatible for assignment. This is by design.
More
The only way to stay compatible is the preserve the generic as well e.g.
class Foo<T> {
model: T
}
class Bar<T>{
model: T
}
const example3: typeof Foo = Bar; // Okay
To complement basarat's answer, here's a working declaration:
private ViewClasses: (new (...args: any[]) => UnitView<{}>)[] = [
TitleView
]

Typescript Create Generic Object

I'm trying to create an object in typescript.
I want to do this generically so that it automatically gets it's default based off of its type
interface IModal {
content: string;
count: number
}
Normally I would declare an Instance of it like so:
var modal: IModal = {
content: '',
count: 0
};
I don't want to have to do this everytime. I want to do this so that the it automatically create an instance of the interface with Typed default i.e
number = 0,
string = '',
boolean = false,
MyCustom = new MyCustom
I want to do something like this:
export class MakeObject {
Make<T> : T = function(iInterface) => {
return default(T);
}
}
But I don't think that will work
Interfaces can extends classes, so you can make it like this:
class DefaultModel {
content = ''
count = 0
}
export interface IModel extends DefaultModel {}
/* since interface generates no code,
* you can also export the default instance using the same name, if you like
*/
export const IModel: IModel = new DefaultModel
// or export the default class, if you're more interested in it
export const IModel = DefaultModel
another way to do it, if you are not against classes, is using abstract classes, in typescript they are as flexible as interfaces (eg: interchangeable with type) and exist at runtime:
abstract class BaseModel {
content = ''
count = 0
}
const modal = new (class extends BaseModel {})
Note that with this approach content and count are not abstract, so while you can still use BaseModel for typecheck, if you need to force subclasses to provide their own values, you may still create an interface to clear the default:
class SomeModel extends BaseModel {} // OK
interface IModel extends BaseModel {}
class SomeModel implements IModel {} // NOT OK: Property 'content' is missing
I want to do this so that the it automatically create an instance of the interface with Typed default i.e
You can't do this magically as interfaces do not exist at runtime. That said you can write code to do this quite easily:
interface Model {
content: string;
count: number
}
function createModel({
// Add defaults
content = '',
count = 0
} = {}): Model {
return { content, count }
}
createModel(); // Okay
createModel({ count: 1 }); // Okay
createModel({ counted: 1 }); // Error: Typo

Data object constructor not called when parsing JSON

I'm getting some data from a server and then parsing it into TypeScript classes. I'm trying use some inheritance - every class needs to be able to report its type.
Here's how that works:
This is the base class
import { PageElementType } from './page-element-type'
export class PageElement {
pageElementType: PageElementType;
constructor(aPageElementType: PageElementType) {
this.pageElementType = aPageElementType;
}
}
This is a derived class
import { PageElement } from './page-element.model'
import { PageElementType } from './page-element-type'
export class Metadata extends PageElement {
id: number;
body: string;
constructor(){
super(PageElementType.metadata);
}
}
Here's the service function I call to parse the data
getExam(){
let exam = this.http.get('http://orangeberry.hopto.org/api/Exam/1')
.map((response:Response) => <Exam>response.json())
.do(data => console.log(data));
return exam;
}
Seems like I'm getting some sort of plain objects. I want meaningful, functional objects that actually follow the class definition. What's the simplest and most straight-forward way of achieving this for my case?
A cast is only a hint for static code analysis but doesn't have any effect at runtime.
If you want the value to become an instance of a class, you need to create such an instance yourself:
.map((response:Response) => new Exam(response.json()))
where the constructor of the Exam class decomposes the JSON to its properties.

Typescript: How to extend two classes?

I want to save my time and reuse common code across classes that extend PIXI classes (a 2d webGl renderer library).
Object Interfaces:
module Game.Core {
export interface IObject {}
export interface IManagedObject extends IObject{
getKeyInManager(key: string): string;
setKeyInManager(key: string): IObject;
}
}
My issue is that the code inside getKeyInManager and setKeyInManager will not change and I want to reuse it, not to duplicate it, here is the implementation:
export class ObjectThatShouldAlsoBeExtended{
private _keyInManager: string;
public getKeyInManager(key: string): string{
return this._keyInManager;
}
public setKeyInManager(key: string): DisplayObject{
this._keyInManager = key;
return this;
}
}
What I want to do is to automatically add, through a Manager.add(), the key used in the manager to reference the object inside the object itself in its property _keyInManager.
So, let's take an example with a Texture. Here goes the TextureManager
module Game.Managers {
export class TextureManager extends Game.Managers.Manager {
public createFromLocalImage(name: string, relativePath: string): Game.Core.Texture{
return this.add(name, Game.Core.Texture.fromImage("/" + relativePath)).get(name);
}
}
}
When I do this.add(), I want the Game.Managers.Manager add() method to call a method which would exist on the object returned by Game.Core.Texture.fromImage("/" + relativePath). This object, in this case would be a Texture:
module Game.Core {
// I must extend PIXI.Texture, but I need to inject the methods in IManagedObject.
export class Texture extends PIXI.Texture {
}
}
I know that IManagedObject is an interface and cannot contain implementation, but I don't know what to write to inject the class ObjectThatShouldAlsoBeExtended inside my Texture class. Knowing that the same process would be required for Sprite, TilingSprite, Layer and more.
I need experienced TypeScript feedback/advice here, it must be possible to do it, but not by multiple extends since only one is possible at the time, I didn't find any other solution.
There is a little known feature in TypeScript that allows you to use Mixins to create re-usable small objects. You can compose these into larger objects using multiple inheritance (multiple inheritance is not allowed for classes, but it is allowed for mixins - which are like interfaces with an associated implenentation).
More information on TypeScript Mixins
I think you could use this technique to share common components between many classes in your game and to re-use many of these components from a single class in your game:
Here is a quick Mixins demo... first, the flavours that you want to mix:
class CanEat {
public eat() {
alert('Munch Munch.');
}
}
class CanSleep {
sleep() {
alert('Zzzzzzz.');
}
}
Then the magic method for Mixin creation (you only need this once somewhere in your program...)
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
if (name !== 'constructor') {
derivedCtor.prototype[name] = baseCtor.prototype[name];
}
});
});
}
And then you can create classes with multiple inheritance from mixin flavours:
class Being implements CanEat, CanSleep {
eat: () => void;
sleep: () => void;
}
applyMixins (Being, [CanEat, CanSleep]);
Note that there is no actual implementation in this class - just enough to make it pass the requirements of the "interfaces". But when we use this class - it all works.
var being = new Being();
// Zzzzzzz...
being.sleep();
I would suggest using the new mixins approach described there: https://blogs.msdn.microsoft.com/typescript/2017/02/22/announcing-typescript-2-2/
This approach is better, than the "applyMixins" approach described by Fenton, because the autocompiler would help you and show all the methods / properties from the both base and 2nd inheritance classes.
This approach might be checked on the TS Playground site.
Here is the implementation:
class MainClass {
testMainClass() {
alert("testMainClass");
}
}
const addSecondInheritance = (BaseClass: { new(...args) }) => {
return class extends BaseClass {
testSecondInheritance() {
alert("testSecondInheritance");
}
}
}
// Prepare the new class, which "inherits" 2 classes (MainClass and the cass declared in the addSecondInheritance method)
const SecondInheritanceClass = addSecondInheritance(MainClass);
// Create object from the new prepared class
const secondInheritanceObj = new SecondInheritanceClass();
secondInheritanceObj.testMainClass();
secondInheritanceObj.testSecondInheritance();
I found an up-to-date & unparalleled solution: https://www.npmjs.com/package/ts-mixer
You are welcome :)
TypeScript supports decorators, and using that feature plus a little library called typescript-mix you can use mixins to have multiple inheritance with just a couple of lines
// The following line is only for intellisense to work
interface Shopperholic extends Buyer, Transportable {}
class Shopperholic {
// The following line is where we "extend" from other 2 classes
#use( Buyer, Transportable ) this
price = 2000;
}
I think there is a much better approach, that allows for solid type-safety and scalability.
First declare interfaces that you want to implement on your target class:
interface IBar {
doBarThings(): void;
}
interface IBazz {
doBazzThings(): void;
}
class Foo implements IBar, IBazz {}
Now we have to add the implementation to the Foo class. We can use class mixins that also implements these interfaces:
class Base {}
type Constructor<I = Base> = new (...args: any[]) => I;
function Bar<T extends Constructor>(constructor: T = Base as any) {
return class extends constructor implements IBar {
public doBarThings() {
console.log("Do bar!");
}
};
}
function Bazz<T extends Constructor>(constructor: T = Base as any) {
return class extends constructor implements IBazz {
public doBazzThings() {
console.log("Do bazz!");
}
};
}
Extend the Foo class with the class mixins:
class Foo extends Bar(Bazz()) implements IBar, IBazz {
public doBarThings() {
super.doBarThings();
console.log("Override mixin");
}
}
const foo = new Foo();
foo.doBazzThings(); // Do bazz!
foo.doBarThings(); // Do bar! // Override mixin
Unfortunately typescript does not support multiple inheritance. Therefore there is no completely trivial answer, you will probably have to restructure your program
Here are a few suggestions:
If this additional class contains behaviour that many of your subclasses share, it makes sense to insert it into the class hierarchy, somewhere at the top. Maybe you could derive the common superclass of Sprite, Texture, Layer, ... from this class ? This would be a good choice, if you can find a good spot in the type hirarchy. But I would not recommend to just insert this class at a random point. Inheritance expresses an "Is a - relationship" e.g. a dog is an animal, a texture is an instance of this class. You would have to ask yourself, if this really models the relationship between the objects in your code. A logical inheritance tree is very valuable
If the additional class does not fit logically into the type hierarchy, you could use aggregation. That means that you add an instance variable of the type of this class to a common superclass of Sprite, Texture, Layer, ... Then you can access the variable with its getter/setter in all subclasses. This models a "Has a - relationship".
You could also convert your class into an interface. Then you could extend the interface with all your classes but would have to implement the methods correctly in each class. This means some code redundancy but in this case not much.
You have to decide for yourself which approach you like best. Personally I would recommend to convert the class to an interface.
One tip: Typescript offers properties, which are syntactic sugar for getters and setters. You might want to take a look at this: http://blogs.microsoft.co.il/gilf/2013/01/22/creating-properties-in-typescript/
A very hacky solution would be to loop through the class you want to inherit from adding the functions one by one to the new parent class
class ChildA {
public static x = 5
}
class ChildB {
public static y = 6
}
class Parent {}
for (const property in ChildA) {
Parent[property] = ChildA[property]
}
for (const property in ChildB) {
Parent[property] = ChildB[property]
}
Parent.x
// 5
Parent.y
// 6
All properties of ChildA and ChildB can now be accessed from the Parent class, however they will not be recognised meaning that you will receive warnings such as Property 'x' does not exist on 'typeof Parent'
In design patterns there is a principle called "favouring composition over inheritance". It says instead of inheriting Class B from Class A ,put an instance of class A inside class B as a property and then you can use functionalities of class A inside class B.
You can see some examples of that here and here.
In my case I used concatenative inheritance.
Maybe for someone this way will be helpful:
class Sprite {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
class Plane extends Sprite {
fly(): string {
return 'I can Fly!'
}
}
class Enemy {
isEnemy = true;
}
class Player {
isPlayer = true;
}
// You can create factory functions to create new instances
const enemyPlane = Object.assign(new Plane(1, 1), new Enemy());
const playerPlane = Object.assign(new Plane(2, 2), new Player());
Also I recommend reading Eric Elliott's articles about js inheritance:
The Heart & Soul of Prototypal OO: Concatenative Inheritance
3 Different Kinds of Prototypal Inheritance
There are so many good answers here already, but i just want to show with an example that you can add additional functionality to the class being extended;
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
if (name !== 'constructor') {
derivedCtor.prototype[name] = baseCtor.prototype[name];
}
});
});
}
class Class1 {
doWork() {
console.log('Working');
}
}
class Class2 {
sleep() {
console.log('Sleeping');
}
}
class FatClass implements Class1, Class2 {
doWork: () => void = () => { };
sleep: () => void = () => { };
x: number = 23;
private _z: number = 80;
get z(): number {
return this._z;
}
set z(newZ) {
this._z = newZ;
}
saySomething(y: string) {
console.log(`Just saying ${y}...`);
}
}
applyMixins(FatClass, [Class1, Class2]);
let fatClass = new FatClass();
fatClass.doWork();
fatClass.saySomething("nothing");
console.log(fatClass.x);
You can call Dynamic Inheritance or Class Factory.
type ClassConstructor<T> = {
new (...args: any[]): T;
};
interface IA {
a: string;
}
interface IB {
b: string;
}
interface IAB extends IA, IB {}
class EmptyClass {}
function GetA<T>(t: ClassConstructor<T> = EmptyClass as any) {
class A extends (t as any) implements IA {
a = 'Default value a';
}
return A as unknown as ClassConstructor<IA & T>;
}
function GetB<T>(t: ClassConstructor<T> = EmptyClass as any) {
class B extends (t as any) implements IB {
b = 'Default value b';
}
return B as unknown as ClassConstructor<IB & T>;
}
class C extends GetA<IB>(GetB()) implements IAB {}
Found a way:
export interface IsA {
aWork(): void
}
export interface IsB {
}
export class A implements IsA {
aWork() { }
}
export class B implements IsB {
bWork() { }
}
export interface IsAB extends A, B {}
export class IsAB {}
Then you can
export class C extends IsAB {}
or even
const c = new IsAB {}

Categories

Resources