Class constructor type in typescript? - class

How can I declare a class type, so that I ensure the object is a constructor of a general class?
In the following example, I want to know which type should I give to AnimalClass so that it could either be Penguin or Lion:
class Animal {
constructor() {
console.log("Animal");
}
}
class Penguin extends Animal {
constructor() {
super();
console.log("Penguin");
}
}
class Lion extends Animal {
constructor() {
super();
console.log("Lion");
}
}
class Zoo {
AnimalClass: class // AnimalClass could be 'Lion' or 'Penguin'
constructor(AnimalClass: class) {
this.AnimalClass = AnimalClass
let Hector = new AnimalClass();
}
}
Of course, the class type does not work, and it would be too general anyway.

Edit: This question was answered in 2016 and is kind of outdated. Look at #Nenad up-to-date answer below.
Solution from typescript interfaces reference:
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick();
}
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
return new ctor(hour, minute);
}
class DigitalClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("beep beep");
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("tick tock");
}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
So the previous example becomes:
interface AnimalConstructor {
new (): Animal;
}
class Animal {
constructor() {
console.log("Animal");
}
}
class Penguin extends Animal {
constructor() {
super();
console.log("Penguin");
}
}
class Lion extends Animal {
constructor() {
super();
console.log("Lion");
}
}
class Zoo {
AnimalClass: AnimalConstructor // AnimalClass can be 'Lion' or 'Penguin'
constructor(AnimalClass: AnimalConstructor) {
this.AnimalClass = AnimalClass
let Hector = new AnimalClass();
}
}

I am not sure if this was possible in TypeScript when the question was originally asked, but my preferred solution is with generics:
class Zoo<T extends Animal> {
constructor(public readonly AnimalClass: new () => T) {
}
}
This way variables penguin and lion infer concrete type Penguin or Lion even in the TypeScript intellisense.
const penguinZoo = new Zoo(Penguin);
const penguin = new penguinZoo.AnimalClass(); // `penguin` is of `Penguin` type.
const lionZoo = new Zoo(Lion);
const lion = new lionZoo.AnimalClass(); // `lion` is `Lion` type.

Like that:
class Zoo {
AnimalClass: typeof Animal;
constructor(AnimalClass: typeof Animal ) {
this.AnimalClass = AnimalClass
let Hector = new AnimalClass();
}
}
Or just:
class Zoo {
constructor(public AnimalClass: typeof Animal ) {
let Hector = new AnimalClass();
}
}
typeof Class is the type of the class constructor. It's preferable to the custom constructor type declaration because it processes static class members properly.
Here's the relevant part of TypeScript docs. Search for the typeof. As a part of a TypeScript type annotation, it means "give me the type of the symbol called Animal" which is the type of the class constructor function in our case.

How can I declare a class type, so that I ensure the object is a constructor of a general class?
A Constructor type could be defined as:
type AConstructorTypeOf<T> = new (...args:any[]) => T;
class A { ... }
function factory(Ctor: AConstructorTypeOf<A>){
return new Ctor();
}
const aInstance = factory(A);

Related

How to type the extension in Dart

I have classes called bird and dog:
class Bird extends Animal with FlyAbility, JumpAbility {
// ...
}
class Dog extends Animal with RunAbility, JumpAbility {
// ...
}
Both extend abstract class Animal and use mixin for their own ability.
Now I like to use some extensions on it to output their information:
extension BirdSheetExporter on Bird {
RowData export() {
return RowData(values: [
// type
CellData(stringValue: 'bird'),
// name
CellData(stringValue: name),
// ...
]);
}
}
extension DogSheetExporter on Dog {
RowData export() {
return RowData(values: [
// type
CellData(stringValue: 'dog'),
// name
CellData(stringValue: name),
// ...
]);
}
}
Their are also other exporter like RawTextExporter:
extension BirdRawTextExporter on Bird {
String export() {
String flyAbility = canFly ? 'can' : "can't";
return 'The bird named $name $flyAbility fly';
}
}
This is user responsibility to choose what extension they want. My question is how to give some interface for these extensions and type it?
Something like:
// extension file: raw_text_exporters.dart
abstract class RawTextExporter {
String export();
}
// I know this is not allowed
extension BirdRawTextExporter implements RawTextExporter on Bird {
// ..
}
// client file: export_animal_raw_text.dart
void exportAnimal(RawTextExporter animal) {
print(animal.export());
}
Or any suggestion for this situation? I have found some work around:
typedef rawTextExporter = String Function();
void exportAnimalByExporter(rawTextExporter exporter) {
print(exporter());
}
The reason I'm not using dynamic is code analyzer will consider the imported file not used.
import 'extensions/raw_text_exporters.dart';
// Analyzer thinks `raw_text_exporters.dart` has nothing to do with this function
void exportAnimal(dynamin animal) {
print(animal.export());
}
// type from `raw_text_exporters.dart` and it will keeps the file
void exportAnimal(RawTextExporter animal) {
print(animal.export());
}
I think the better solution will be dependency injection:
class Bird extends Animal with FlyAbility, JumpAbility, Exportable {
// ...
}
mixin Exportable {
T export<T>(Exporter exporter) {
return exporter.export(bird);
}
}
And the exporter's interface will be like:
abstract class Exporter<T> {
T export(Animal animal);
}
class RawTextExporter<String> {
#override
String export(Animal animal) {
final species = animal is Bird ? 'bird' : 'unknown';
return 'The $species named $name ...';
}
}

Class like a type in interface of Typescript

Is it possible to use Class like a type in interface ? For example, I have a class Animal, can I use something like:
interface I {
object: Animal
}
I've got en error on this situation:
class A {
public static foo(text: string): string {
return text;
}
}
interface IA {
testProp: A;
otherProp: any;
}
class B {
constructor(prop: IA) {
console.log(prop.otherProp);
console.log(prop.testProp.foo('hello!'));
}
}
TS2339: Property 'foo' does not exist on type 'A'
You need to use typeof A:
class A {
public static foo(text: string): string {
return text;
}
}
interface IA {
testProp: typeof A;
otherProp: any;
}
class B {
constructor(prop: IA) {
console.log(prop.otherProp);
console.log(prop.testProp.foo('hello!'));
}
}
The problem in your code is that the foo method is static. Static can only be used on classes not object.
In your case:
A.foo("hello); //works
new A().foo("hello"); //doesn't work since it's an instance of A

TypeScript: How to get a subclass's method to return parent this

I am trying to figure out how to get this to work correctly:
class A {
john(): B {
return this; // <-- ERROR HERE
}
}
class B extends A {
joe(): B {
return this;
}
}
So I can do method chaining:
let instance = new B();
instance.john().joe();
Of course, TypeScript complains that this doesn't match B's type.
Simply use the this keyword as the return type of methods that return this:
class A {
john(): this {
return this;
}
}
class B extends A {
joe(): this {
return this;
}
}
let instance = new B();
instance.john().joe();
You can also omit the return types. TypeScript will infer the return types as this because the methods return this:
class A {
john() {
return this;
}
}
class B extends A {
joe() {
return this;
}
}
This feature is called Polymorphic this types and was introduced in TypeScript 1.7. See the GitHub PR for details.

overriding static vars in subclasses swift 1.2

unlike class var, where they can overridden in subclasses, I believe same applies to static as well but unfortunately not. Here's an example
public class A {
private static let NAME: String = "A"
}
public class B: A {
private static let NAME: String = "B" //error
}
in my opinion static means association with that particular class, so in the above example B should get it's own space to redefine that variable as it's associated to B only, I'm reverting to stored properties unless someone has a better solution.
You can use computed properties:
class A {
class var Name: String {
return "A"
}
}
class B: A {
override class var Name: String {
return "B"
}
}
Usage:
print(A.Name) // "A"
print(B.Name) // "B"
The documentation says:
“
static
” methods and properties are now allowed in classes (as an alias for “
class final
”).
So it is final, which means you cannot override it.
As suggested, you cannot override static variables but can use class (static) function to override.
class A {
class func StaticValue() -> AnyObject {
return "I am a String"
}
}
class B: A {
override class func StaticValue() -> AnyObject {
return 2
}
}
You can have computed class properties:
public class A {
class var NAME: String {
return "A"
}
}
public class B {
class var NAME: String {
return "B"
}
}
If need be, you could even "branch" to a stored property in the subclass:
public class A {
// Needs to be overriden in subclass
class var NAME: String {
return "A"
}
}
public class B {
class var NAME: String {
return B.storedName
}
static var storedName: String = defineName()
func defineName() -> String {
// You could do something more complex here
return "B"
}
}

Implementing an indexer in a class in TypeScript

Is it currently possible to implement an indexer on a class in TypeScript?
class MyCollection {
[name: string]: MyType;
}
This doesn't compile. I can specify an indexer on an interface, of course, but I need methods on this type as well as the indexer, so an interface won't suffice.
Thanks.
You cannot implement a class with an indexer. You can create an interface, but that interface cannot be implemented by a class. It can be implemented in plain JavaScript, and you can specify functions as well as the indexer on the interface:
class MyType {
constructor(public someVal: string) {
}
}
interface MyCollection {
[name: string]: MyType;
}
var collection: MyCollection = {};
collection['First'] = new MyType('Val');
collection['Second'] = new MyType('Another');
var a = collection['First'];
alert(a.someVal);
This is an old question, for those looking for the answer: now it's possible to define a indexed property like:
let lookup : {[key:string]:AnyType};
the signature of the key must be either string or integer see:
Interfaces on www.typescriptlang.org
Is not possible to define an indexed property getter/setter in a class but you can "simulate" that in a way like this using Proxy:
class IndexedPropSample {
[name: string | symbol]: any;
private static indexedHandler: ProxyHandler<IndexedPropSample> = {
get(target, property) {
return target[property];
},
set(target, property, value): boolean {
target[property] = value;
return true;
}
};
constructor() {
return new Proxy(this, IndexedPropSample.indexedHandler);
}
readIndexedProp = (prop: string | symbol): any => {
return this[prop];
}
}
var test = new IndexedPropSample();
test["propCustom"] = "valueCustom";
console.log(test["propCustom"]); // "valueCustom"
console.log(test.readIndexedProp("propCustom")); // "valueCustom"
console.log(test instanceof IndexedPropSample); // true
console.log(Object.keys(test)); // ["propCustom", "readIndexedProp"]
you can try it in Typescript Playground