What does <T = unknown> mean in a generic class in TypeScript? - typescript-generics

In some code I am reading, I see a class declared thus:
export abstract class ClassName<T = unknown> implements InterfaceName { … }
I am familiar with unconstrained generics (class C<T> {…) and constrained generics (class C<T extends S> {…), but what does = unknown mean, and what effect does it have compared to no constraint at all?
Surely, absent an extends, code within the generic class must treat T as if it were unknown already? And if <T = S> means to pin type T to exactly type S then why make it a generic at all?

This is default parameter type. It is type, that is resolved, when the type constructor is given without the type parameters.
Consider this class definition:
export class Class<T = unknown> { }
Then you can create instances either with explicit type for T or without.
const x = new Class()
const y = new Class<unknown>()
const z = new Class<number>()
Here, type of x and y will both be Class<unknown>, whereas z would be Class<number>. So it means, that declaring class with <T = unknown> is the same as <T>.
So for this particular case, it doesn't make much sense, but if you choose anything else than unknown, then it will serve as default type parameter.
Note, that it doesn't serve as constraint, that means that if you declare
export class ClassB<T = string> { }
you can still declare:
const z = new ClassB<number>()

Related

Restrict emits from EventEmitter3

Related to the accepted answer: https://stackoverflow.com/a/63639280/17928771
EventEmitter3 is a generic class that takes an (as one example) Interface of Events/Handlers. I'm trying to restrict IRaceManager emits to IRaceEvents. I have tried:
Attempt 1.
class POSRaceManager implements IRaceManager extends EventEmitter<IRaceEvents> {}
let raceManager: IRaceManager = new POSRaceManager();
raceManager.emit('moo'); // error, no `emit` on IRaceManager
Attempt 2.
interface IRaceManager extends EventEmitter<IRaceEvents> {} // TS2749: 'EventEmitter' refers to a value, but is being used as a type here. Did you mean 'typeof EventEmitter'?
The following attempt works, but doesn't limit the emit or on to the IRaceEvents (or an extension of IRaceEvents)
Attempt 3.
This fails:
type RaceEventEmitterType<T extends IRaceEvents> = InstanceType<typeof EventEmitter>;
type IRaceManager<T extends IRaceEvents> = RaceEventEmitterType<T>;
let raceManager: IRaceManager = new POSRaceManager<IRaceEvents>;
raceManager.emit("moo"); // no error because of `InstanceType<typeof EventEmitter>`
Final attempt.
type RaceEventEmitterType<T extends IRaceEvents> = InstanceType<typeof EventEmitter<IRaceEvents>>; // Need to investigate what this does, actually. I overlooked a syntax error before.
Any ideas to restrict emissions from raceManager<?> to T extends IRaceEvents only?
Edit:
I am currently settled on the following (which works, I am just wondering if there is a way to resolve as above):
type RaceEventEmitter<T extends IRaceEvents> = InstanceType<typeof EventEmitter>;
export type IRaceManager<T extends IRaceEvents> = RaceEventEmitter<T>;
class POSRaceManager extends EventEmitter<IRaceEvents> implements IRaceManager<IRaceEvents>;

Can't extends imported class

class A {}
class B extends A {}
the code above work fine but when I try to do the same with an imported class, I't does not work.
declare module 'a' {
declare export class A {}
}
import typeof { A } from 'a';
class B extends A {}
Cannot reference type A [1] from a value position
can anyone solve this ?
https://flow.org/try/#0CYUwxgNghgTiAEBbA9sArhBByKX4G8AoeeUSWBEADwAdkYAXecgZxfgEECBfQ3wgJaI6jeAwCeNEMgBmBTvG4ACmTGSJ4OLAG5ChVuwBC8agxAA7YOy75uQA
When you define a class you actually define both a type and a Javascript class, and both have the same name. Types only exist at compile time - they have no runtime representation. In your example you imported the type, but not the runtime value.
// imports the type only
import typeof { A } from 'a'
// imports both the type and the runtime value
import { A } from 'a'
The type informs Flow what the class does for purposes of type checking. But the runtime value is what defines the implementation to be executed. The compiled program must have a reference to the runtime value to instantiate or to extend a class. In other words, remove the typeof keyword and it should work.

How to set type of a class using local variable that holds that type?

I've got a method that does the following:
val type = AClass::class.java.methods[0].parameters[0].type
val toDeserialise = SecondClass<type>()
My SecondClass is:
class SecondClass<T : Any> {
lateinit var p1: T
}
But this doesn't work. Basically, I want to take a type of a method's parameter and pass it into a SecondClass. Do you know how to implement this?
The AClass is:
class AClass{
fun myMethod(param1: String, param2: UUID)
}
So, I want the
val type
to be String, which I pass into SecondClass.
Reflection is resolved at runtime, and generic types are resolved at compile time.
The SecondClass is subject to type erasure, so what you want to do is essentially impossible.
Even if you try to work around it with a switch:
val type = AClass::class.java.methods[0].parameters[0].type
val toDeserialize = when(type){
String::class.java -> SecondClass<String>()
else -> SecondClass<Any>()
}
This will bring you no benefit since you won't be able to check the actual type of SecondClass.

flowtype: $Subtype and typeof fail when used in generic types

It appears that class types in flow always refer to instances of that class and one uses typeof to refer to the actual class itself. So, if I want a variable to refer to a subclass (not an instance) of a base class, I can do:
class MyBaseClass {}
class MySubClass extends MyBaseClass {}
let a: $Subtype<MyBaseClass> = MySubClass; // fails
let b: $Subtype<MyBaseClass> = new MySubClass(); // works, but I don't want this.
let c: $Subtype<typeof MyBaseClass> = MySubClass; // works! Ok, we're good
However, I can't seem to do this with type parameters! For example, the following:
type GenericSubclass<T> = $Subtype<typeof T>;
// fails with `^ identifier `T`. Could not resolve name`
If I try the following Typescript trick (see Generic and typeof T in the parameters), it also fails:
type ValidSubclass<T> = { new(): T };
const c: ValidSubclass<BaseClass> = MySubClass;
// fails with: property `new`. Property not found in statics of MySubClass
Note that I tried new, __proto__ and constructor.
What gives? Is there a workaround?
typeof MyBaseClass
is
Class<MyBaseClass>
so you can do
type GenericSubclass<T> = $Subtype<Class<T>>;

Scala: How to know if a class is an enumeration; isInstanceOf[Enumeration] doesn't work

I'm in scala writing a serializer that saves an object (or Model) to the database (for app engine), and I need to treat some fields as special cases. For example, if the field is of type Array[Byte], I Save it as a blob. And I need to treat Enumerations as special cases too, but I can't find out how to know if a type is an enumeration.
For example:
object UserType extends Enumeration {
val Anonym, Registered, Admin, Super = Value
}
var value = UserType.Admin
value.isInstanceOf[Enumeration] // this returns false
Neither I can do value.isInstanceOf[Enumeration.Value] since Value is private... anyway I think that would return false too.
Any idea?
Thanks!
value.isInstanceOf[Enumeration$Value]
You could figure this out using these methods:
scala> value.getClass
res102: java.lang.Class[_] = class scala.Enumeration$Val
scala> value.getClass.getSuperclass
res103: java.lang.Class[_ >: ?0] = class scala.Enumeration$Value
scala> value.getClass.getSuperclass.getSuperclass
res104: java.lang.Class[_ >: ?0] = class java.lang.Object

Resources