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

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.

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

Swift Generics - Returning Subclasses of Generic Class

I'm looking to return any number of subclasses of a generic class in a single method but am unsure of how to format the function in order to do so. Below is a sample playground of my issue.
import UIKit
class TableViewCellData {
}
class SubTableViewCellData: TableViewCellData {
}
protocol ClassProtocol {
associatedtype DataType: TableViewCellData
}
class SuperDataClass {
}
class SubDataClass: SuperDataClass {
}
class SuperClass<T: SuperDataClass>: UITableViewCell, ClassProtocol {
typealias DataType = TableViewCellData
}
class SubClass: SuperClass<SubDataClass> {
typealias DataType = SubTableViewCellData
}
class Example {
func test<T: SuperDataClass>(object: SuperClass<T>) {
print(object)
}
func returnType() -> SuperClass<SuperDataClass>.Type? {
var objectToReturn: SuperClass<SuperDataClass>.Type?
if true {
objectToReturn = SuperClass<SuperDataClass>.self
} else {
objectToReturn = SubClass.self
}
return objectToReturn
}
}
let object = SubClass()
Example().test(object: object)
if let object2 = Example().returnType() {
print(object2)
}
The code above will not run in a playground, you will see an error pointing to line 42 objectToReturn = SubClass.self because error: cannot assign value of type 'SubClass.Type' to type 'SuperClass<SuperDataClass>.Type?'. Is there a way to change the object type to expect the generic and any subclass version of the generic?

Class constructor type in typescript?

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

How to add operator extension as a part of context of the specific class without subclassing?

I'm trying to utilize operators for Wicket, which is painfully verbose.
My most wanted feature is to use an unary "+" to add() a component.
But it will be needed to work inside context of every MarkupContainer descendants.
Use should be like this:
class SomePage() : WebPage() {
init {
// SomePage{} context
+Label("someLabel","Some label")
// instead of this.add(Label("someLabel","Some label"))
+object : StatelessForm<Unit>("someForm") {
init {
// StatelessForm{} context
+Label("fieldLabel","Field label")
+RequiredTextField("someField")
}
}
}
}
Is it possible to implement this now without subclassing everything? Some imaginary syntax of what i want:
extend org.apache.wicket.MarkupContainer {
operator fun<T: Component> T.unaryPlus():T {
// add() is called as a method of a MarkupContainer instance
add(this) // this#MarkupContainer.add(this#unaryPlus)
return this
}
}
https://kotlinlang.org/docs/reference/extensions.html
https://kotlinlang.org/docs/reference/operator-overloading.html
Using the unaryPlus operator (+Component) is harder in this context since, as the unary implies, it's a single operand operator (single input). There's a sort of hacky solution though:
class ExtOf<out T : MarkupContainer>(val self: T) {
companion object {
private val lastConfiguredContainer = ThreadLocal<ExtOf<MarkupContainer>?>()
fun <T : MarkupContainer> configure(container: T, configurer: ExtOf<T>.() -> Any?): T {
val currentLast = lastConfiguredContainer.get()
try {
val newCurrent = ExtOf(container)
lastConfiguredContainer.set(newCurrent)
newCurrent.configurer()
} finally {
lastConfiguredContainer.set(currentLast)
}
return container
}
}
operator fun <T2 : Component> T2.unaryPlus(): T2 {
val container = lastConfiguredContainer.get()
container!!.self.add(this) //TODO throw a nice exception explaining how ot use the `configure`
return this
}
}
fun <T : MarkupContainer> T.configure(configurer: ExtOf<T>.() -> Any?) = ExtOf.configure(this, configurer)
The above maintains information about last configured MarkupContainer in a ThreadLocal private variable that is used to supply the receiver of add method.
You can then write:
class SomePage() : WebPage() {
init {
configure {
+Label("someLabel", "Some label")
+StatelessForm<Unit>("someForm").configure {
// StatelessForm{} context
+Label("fieldLabel", "Field label")
+RequiredTextField<Long>("someField")
}
}
}
}
As I mentioned above the solution while works is far from being pretty. It can be confusing (as often overloaded operators are) so I'd advice using the regular add like so:
class SomePage() : WebPage() {
init {
add(
Label("someLabel", "Some label"),
StatelessForm<Unit>("someForm").apply {
// StatelessForm{} context
add(
Label("fieldLabel", "Field label"),
RequiredTextField<Long>("someField")
)
}
}
}
}
I guess ideally there would be a library similar to anko but for wicket.
I believe it's not possible
The problem is that unaryPlus operator fun cannot have any params. You want to use unaryPlus inside MarkupContainer class, but you also want to add component (other class, other reference with technicly could be a param)
The only way I see it could work would be with subclassing (Component subclass MarkupContainer) and unaryPlus perator extension to MarkupContainer
class Label(name: String, b: String) : Component() {}
open class MarkupContainer() {
fun add(component: Component) {
println("added" + this)
}
}
open class Component() : MarkupContainer() {}
open class StatelessForm<T>(name: String) : Component()
operator fun Component.unaryPlus() {
add(this)
}
class SomePage() : Component() {
init {
add(Label("someLabel", "Some label"))
+Label("someLabel", "Some label")
+object : StatelessForm<Unit>("someForm") {
init {
// StatelessForm{} context
+Label("fieldLabel", "Field label")
}
}
}
}