You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This issue was previously reported as bug at codeplex, now trying to submit as suggestion.
Below, the Collection.model field is supposed to accept type of TModel rather than instance of TModel:
classModel{}classCollection<TModelextendsModel>{model: typeofTModel;//Error: Could not find symbold 'TModel'.models: TModel[];}classMyModelextendsModel{}classMyCollectionextendsCollection<MyModel>{// This is what we wantmodel: typeofMyModel=MyModel;// This is what we don't want//model: MyModel = new MyModel;}varcoll=newMyCollection();varmodelType=coll.model;varnewModel=newmodelType();
Use of typeof keyword seems to be appropriate here, but compiler doesn't understand typeof of generic type. If we said model: typeof Model; it would compile but that is not exactly what we want.
Some of the workarounds we found are the followings:
typeof takes a value and produces the type that the value has. Generic type parameters are types; they are already types and there's no need to have an operator to convert them to types.
Your workarounds are actually the correct design because they model the constraint you have -- a zero-argument constructor function for the given type.
The first workaround actually causes problems with statics:
classModel{staticdoStuff(){}}classCollection<TModelextendsModel>{// With this workaround, you also have to hack in doStuff() here which causes a lot of duplication.model: {new(): TModel;};models: TModel[];doStuffToModel(){// This is okay because we hacked in "new(): TModel"varmodel=newthis.model();// This is not okay because we didn't hack in "doStuff(): void"this.model.doStuff();}}
The second workaround is a lot better but also problematic:
classModel{}classCollection<TModelConstructorextendstypeofModel,TModelextendsModel>{model: TModelConstructor;models: TModel[];staticcreateCoolInstance<TModelConstructorextendstypeofModel,TModelextendsModel>(ctor: TModelConstructor): TModel{// "Type 'Model' is not assignable to type 'TModel'." so you have to "<TModel>new ctor();" which seems pretty unnecessaryreturnnewctor();}}
Let me explain what I think about this with some pseudo code:
classMyClass{staticfoo(){};bar(){};}// That does something like:typeMyClassInstanceType={bar: ()=>void};typeMyClassConstructorType={new(): MyClassInstanceType;foo(): void};varMyClassValue=function(){};MyClassValue.prototype.bar=function(){};MyClassValue.foo=function(){};// So if typeof was a function, it would be something like:functiontypeof(classValue: Value): Type{returnClassValueToConstructorTypeMap[classValue];}
My question is, why does typeof take a value? Here is an example:
The first "MyClass" refers to MyClassInstanceType and the second one refers to MyClassValue.
On the second line, couldn't the first "MyClass" refer to MyClassInstanceType again and get MyClassConstructorType from that?
If changing typeof to or overloading it with "InstanceType => ConstructorType" is breaking, I think we could easily have another operator for it. (constructorof? staticof?)
autoConstructor<T>(ctor: constructorofT): T{returnnewctor();}varinst;/* Works */classMyClass{}inst=autoConstructor(MyClass);/* Should these work? */inst=autoConstructor<number>(Number);inst=autoConstructor(Number);inst=autoConstructor<string>(String);inst=autoConstructor(String);inst=autoConstructor(Date);/* Maybe: */// OkautoConstructor<TextendsMyClass>(ctor: constructorofT)......
// Error: Types given to 'constructorof' must extend a class type autoConstructor<T>(ctor: constructorofT)......
Note that new Number() does not produce a number (it's not primitive).
You can already write this:
interfaceconstructorof<T>{new(): T;}functionautoConstructor<T>(ctor: constructorof<T>): T{returnnewctor();}varinst;/* Works */classMyClass{}letc=autoConstructor(MyClass);// c: MyClass/* Should these work? */inst=autoConstructor<Number>(Number);inst=autoConstructor(Number);inst=autoConstructor<String>(String);inst=autoConstructor(String);inst=autoConstructor(Date);
That doesn't provide the actual type for the constructor:
classMyClass{staticfoo(){};}interfaceconstructorof<T>{new(): T;}functionautoConstructor<TextendsMyClass>(ctor: constructorof<T>): T{// No foo herector.foo();returnnewctor();}
What is the reason for having typeof? Wouldn't constructorof cover all use cases of typeof and more? (And also without the created confusion with JavaScript's own typeof.)
There can be more than one constructor function for any given type.
interfaceSomeObj{ ... }declarevarctor1: {new(): SomeObj;foo;}declarevarctor2: {new(): SomeObj;bar;}functionmagic<TextendsSomeObj>(ctor: constructorof<T>){// can I use ctor.foo here? ctor.bar?}
typeof exists so that, given a value of some type that is otherwise anonymous, you refer to that anonymous type without having to rewrite it yourself.
Can there be more than one constructor function for any given class type? If not then a constructorof keyword that only works with class types could solve the issue.
classMyClass{staticfoo(){};}/* MyClass was declared with the `class` keyword. We can be sure that it has one constructor. We allow constructorof to be used with it.*/varctor: constructorofMyClass=MyClass;
Ruan Mendes (@juanmendes) your return type should be new(payload: T) => TypedAction<T>, not TypedAction<T>. You've declared that you return the instance type but you've provided a constructor type
I don't understand why the typeof operator should not be applied on type arguments. The language specification states in section 4.18.6:
The 'typeof' operator takes an operand of any type and produces a value of the String primitive type. In
positions where a type is expected, 'typeof' can also be used in a type query (section 3.8.10) to produce
the type of an expression.
So why is something like the followin not allowed?
getConstructor<T>( instance: T )
{
return instance.constructor as typeof T;
}
typeof X produces the type of the valueX. There's no value named T, so the "typeof the value T" question makes no sense. It's like asking how much blue weighs.
classMyClass{}letx: typeofMyClass;// MyClass is a value here
x is a variable whose type is the type of the value named MyClass. A class definition introduces a name in the type space that refers to the type of instances of that class, and a name in the value space that refers to the class value (the thing that gets newed).
Leonardo Garcia Crespo (@leoasis) the type argument belongs to the instance side of the class. typeof operator obtains the type of the static side of the class.
Yeah, I know, that's what I wanted to show in the example. In my particular use case, I'm using React, and I have an InfiniteScrollList component that is parametrized on the type of the item it accepts. We did this to force the same type you provide to be the same type you would expect in any callback prop, instead of it being any and making the user specify the type:
The problem there is that when I define the component with a generic type, I can no longer use it in JSX directly, since I cannot write
Couldn't find another way to make that work without creating an empty class that inherits from the parametrized one. That works, but I would like to avoid creating a class (which will be created at runtime) just to enforce type safety.
The workaround mentioned by Salvatore Previti (@SalvatorePreviti) and the FAQ only works on classes that don't have any static methods, as the return type is only a constructor.
I can't seem to come up with a decent workaround that would even allow for an identity function that accepts and returns a constructor.
abstractclassAnimal<T>{staticidentity<T>(T: typeofAnimal<T>) : typeofT;// this doesn't work}classDogextendsAnimal<Dog>{}Animal.identity(Dog)// => Dog
A more concrete (and realistic) use-case would be a static method that creates a new copy of the class/constructor (possibly with a different set of static properties, or extra static methods). This is a pattern often seen in ORMs, notably Sequelize's Model.scope() method.
If anybody has ideas about similar ways to achieve the same result whilst working within the boundaries of TypeScript's existing type system, let's get them documented!
I'm having the same problem, and basically ended up chain casting to any and typeof Model:
// This class is in a 3rd party library:abstractclassModel<T>{staticcount(): number;}interfaceOptions<TextendsModel<T>>{target: new()=>T;}// `options` type is `Options<T>`constmodel=options.targetasanyastypeofModel;returnmodel.count();
Activity
RyanCavanaugh commentedon Jul 23, 2014
typeoftakes a value and produces the type that the value has. Generic type parameters are types; they are already types and there's no need to have an operator to convert them to types.Your workarounds are actually the correct design because they model the constraint you have -- a zero-argument constructor function for the given type.
See also https://typescript.codeplex.com/discussions/456796
omidkrad commentedon Jul 23, 2014
The link was very informative. Thank you.
AlicanC commentedon Jul 17, 2015
The first workaround actually causes problems with statics:
The second workaround is a lot better but also problematic:
Let me explain what I think about this with some pseudo code:
My question is, why does
typeoftake a value? Here is an example:The first "MyClass" refers to
MyClassInstanceTypeand the second one refers toMyClassValue.On the second line, couldn't the first "MyClass" refer to
MyClassInstanceTypeagain and getMyClassConstructorTypefrom that?If changing
typeofto or overloading it with "InstanceType => ConstructorType" is breaking, I think we could easily have another operator for it. (constructorof?staticof?)AlicanC commentedon Jul 17, 2015
And some examples would be:
RyanCavanaugh commentedon Jul 17, 2015
Note that
new Number()does not produce anumber(it's not primitive).You can already write this:
AlicanC commentedon Jul 17, 2015
That doesn't provide the actual type for the constructor:
What is the reason for having
typeof? Wouldn'tconstructorofcover all use cases oftypeofand more? (And also without the created confusion with JavaScript's owntypeof.)RyanCavanaugh commentedon Jul 17, 2015
There can be more than one constructor function for any given type.
typeofexists so that, given a value of some type that is otherwise anonymous, you refer to that anonymous type without having to rewrite it yourself.AlicanC commentedon Jul 17, 2015
Can there be more than one constructor function for any given class type? If not then a
constructorofkeyword that only works with class types could solve the issue.RyanCavanaugh commentedon Jul 17, 2015
Yes.
AlicanC commentedon Jul 17, 2015
Yes, but
typeofdoesn't care about that:The
constructorofimplementation can do the same.RyanCavanaugh commentedon Jul 21, 2015
I'm still not sure what you're proposing that is meaningfully different from
typeofornew() => T.michaelmesser commentedon Jun 9, 2016
Enum don't have constructors so
new() => Tdoes not work7 remaining items
RyanCavanaugh commentedon Mar 24, 2017
Ruan Mendes (@juanmendes) your return type should be
new(payload: T) => TypedAction<T>, notTypedAction<T>. You've declared that you return the instance type but you've provided a constructor typejuanmendes commentedon Mar 24, 2017
Thanks Ryan, it mostly works, the problem there is that the return of
createTypedActionConstructoris not a true type, that is, I can't cast to it.markusmauch commentedon Aug 11, 2017
I don't understand why the typeof operator should not be applied on type arguments. The language specification states in section 4.18.6:
The 'typeof' operator takes an operand of any type and produces a value of the String primitive type. In
positions where a type is expected, 'typeof' can also be used in a type query (section 3.8.10) to produce
the type of an expression.
So why is something like the followin not allowed?
RyanCavanaugh commentedon Aug 11, 2017
typeof Xproduces the type of the valueX. There's no value namedT, so the "typeof the value T" question makes no sense. It's like asking how much blue weighs.markusmauch commentedon Aug 12, 2017
Ryan Cavanaugh (@RyanCavanaugh): But if so, why do the specs state that typeof can be used in a type query?
aluanhaddad commentedon Aug 12, 2017
Markus Mauch (@markusmauch)
xis a variable whose type is the type of the value namedMyClass. Aclassdefinition introduces a name in the type space that refers to the type of instances of that class, and a name in the value space that refers to theclassvalue (the thing that getsnewed).markusmauch commentedon Aug 13, 2017
Aluan Haddad (@aluanhaddad)
Well okay, MyClass is a value then. But how do I get the type of the constructor function of the type MyClass?
aluanhaddad commentedon Aug 13, 2017
Markus Mauch (@markusmauch) by writing
typeof MyClassin type position. Just as you had done.leoasis commentedon Sep 21, 2017
Is there a reason
typeof MyClass<SpecificType>is not supported when I have it defined asclass MyClass<T> { ... }?I'd like to get the type of the class with a specific type, but without having to create a class that inherits from it:
aluanhaddad commentedon Oct 4, 2017
Leonardo Garcia Crespo (@leoasis) the type argument belongs to the instance side of the class.
typeofoperator obtains the type of the static side of the class.leoasis commentedon Oct 4, 2017
Yeah, I know, that's what I wanted to show in the example. In my particular use case, I'm using React, and I have an
InfiniteScrollListcomponent that is parametrized on the type of the item it accepts. We did this to force the same type you provide to be the same type you would expect in any callback prop, instead of it beinganyand making the user specify the type:The problem there is that when I define the component with a generic type, I can no longer use it in JSX directly, since I cannot write
So I tried doing something like this:
but that didn't work.
Couldn't find another way to make that work without creating an empty class that inherits from the parametrized one. That works, but I would like to avoid creating a class (which will be created at runtime) just to enforce type safety.
schmod commentedon Nov 7, 2017
The workaround mentioned by Salvatore Previti (@SalvatorePreviti) and the FAQ only works on classes that don't have any static methods, as the return type is only a constructor.
I can't seem to come up with a decent workaround that would even allow for an identity function that accepts and returns a constructor.
A more concrete (and realistic) use-case would be a static method that creates a new copy of the class/constructor (possibly with a different set of static properties, or extra static methods). This is a pattern often seen in ORMs, notably Sequelize's
Model.scope()method.If anybody has ideas about similar ways to achieve the same result whilst working within the boundaries of TypeScript's existing type system, let's get them documented!
aluanhaddad commentedon Nov 7, 2017
Or even
class Animal<T> { static identity<T extends new (...args: any[]) => Animal<any>>(this: T) { return this; } static create<T extends Animal<any>>(this: new (...args: any[]) => T) { return new this(); } } class Dog extends Animal<Dog> {} Dog.identity(); // => typeof Dog Dog.create(); // => Dogalfaproject commentedon Mar 4, 2018
I'm having the same problem, and basically ended up chain casting to
anyandtypeof Model: