-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Typing is clumsy when instancing using class objects derived from an abstract class #9302
Comments
I think what you want is this? let classes5 = new Map<string, new() => A>([["b",B], ["c", C]]); // OK
var instance5 = new (classes5.get("c"))(); // OK, instance5: A |
@RyanCavanaugh thanks, your example solves this problem! Just a detail, there is a way to reference the abstract class A { a: any
constructor (x: number, y: number) {}}
class B extends A { b: any}
class C extends A { c: any}
let classes5 = new Map<string, new(x : number, y : number) => A>([["b",B], ["c", C]]); // OK
// must repeat args signature ^^^^^^^^^^^^^^^^^^^^^^
var instance5 = new (classes5.get("c"))(1,2); // OK, instance5: A
// Is possible to reference the A constructor signature? something like this:
let classes6 = new Map<string, typeof A.new>([["b",B], ["c", C]]); Thanks for your reply. |
Unfortunately there isn't |
It's interesting to note that using an object literal might be more typesafe and refactoring friendly than using a abstract class A { a: any}
class B extends A { b: any}
class C extends A { c: any}
let classes = { B, C };
let instance = new classes.B() // inferred type = B
instance.a // ok, because .a is in B |
@aluanhaddad that works, but not when you use an string based key (eg. one taken from a config file). In your example |
The keys are strings, I'm simply using concise object literal notation for brevity and in order to leverage refactoring. It's true that if you're using an index signature with a string literal, it becomes more difficult, but I don't see how it is any worse than an array, and at least it makes any necessary casting more intuitive. But you could add an index signature like [key: string]: B | C; or [key: string]: new () => A; Then again that kind of defeats the thing you were trying to achieve in the first place. When the keys are not constants, it's easy to run into this sort of thing. |
@aluanhaddad: let me check if I get you, given this: let classes1 = new Map<string, new() => A>([["B", B], ["C", C]]);
let classes2 : {[k : string] : new() => A } = { B, C };
|
Actually, I think this works better, let classes = { B, C };
let CPrime = classes["C"]; // CPrime has the same type as C
let instance = new C(); // instance is an instance of C
console.log(instance.c); The problem is the config file. The constant value does not seem to propagate across module boundaries. Forgot my idea of about using a .ts configuration file. It only seems to work within one file. I had a thought that you could do import { classKey } from "./config"; // classKey is defined as "export const classKey = 'C';"
let CPrime = classes[classKey]; // Unfortunately has type any Edit: It's not related to modules, it has to do with string literal type inference. Even if I give a variable an explicit string literal type, it does not flow from the variable to the index signature. There are a lot of issues related to this. |
@aluanhaddad when I said "a config file" I meant an arbitrary JSON document loaded at runtime, not an imported "config" module. But yes, I think you are pointing to another valid issue. |
@nahuel that probably should have been obvious to me because otherwise they wouldn't have been much of a point to the scenario you described. Sorry if I got the issue off track. I don't see how TypeScript could provide this however, without compile time constants. A run-time scenario like you mention, where the type is decided based on dynamically imported configuration is going to have to do type assertions. There is an issue tracking type providers which may be closer to what you're looking for #3136 |
Suppose you have an abstract class
A
and two concrete clasesB
,C
derived fromA
, and you want to storeB
andC
as first-class values to make instances from these values:It will be nice if the previous example inferred the
instance
type asA
instead of(B | C)
. But it works.The problem comes when TS can't infer the
classes
type, like when using aMap
instead of an array:classes2
doesn't work because TS can't infer the type.classes3
doesn't work because you can't use it for instancing.classes4
example worked but is very clumsy for a large number of classes, you need to type(typeof B | typeof C | ...)
for all the classes involved. Maybe this is acceptable but I think there is a need to be able of defining (and inferring) a type like "all concrete classes derived from A".Below is the same example but using a concrete base class. Note in this case you can use
<typeof A>
as the type of the array / map values:TypeScript Version:
1.9.0-dev.20160616-1.0
The text was updated successfully, but these errors were encountered: