If I create a generic class
class Generic<T> {
prop: T;
}
It won't allow me to type something without specifying T in the type
// Generic type 'Generic<T>' requires 1 type argument(s).
const val: Generic = new Generic();
But if I use type inference, it doesn't complain, it lets me instantiate it
const val = new Generic();
// field prop is not of type any, we can't access any properties on it
// val.prop.anything -> Compiler error
// val.prop.toString() -> No error, is it a string?
// val.prop.length -> Compiler error, not a string, just assumes that everything has a toString (based on boxing)
Is this behavior specified? What is the reasoning behind this?
Background
Angular2 has an EventEmitter which requires a type of the argument for the event. However, for some events, you don't pass any arguments, in which case we had been using EventEmitter<void>. However, I just noticed that you can just define emitters without specifying type and new EventEmitter() works. The drawback of this approach is that the compiler won't complain if you pass an argument to emitter.emit('something'). This is not what I'm interested in, just background so readers can understand where the question came from.
Playground https://www.typescriptlang.org/play/#src=class%20Generic%3CT%3E%20%7B%0D%0A%20%20%20%20prop%3A%20T%3B%0D%0A%7D%0D%0A%0D%0Aconst%20val%3A%20Generic%20%3D%20new%20Generic()%3B%0D%0A%0D%0Aconst%20val2%20%3D%20new%20Generic()%3B%0D%0A%0D%0Aval2.prop.test%20%3D%201%3B
The following two statements are equivalent...
const val: Generic<{}> = new Generic();
const val2 = new Generic();
In the first case, the type argument that is omitted on the right-hand side is inferred from the type on the left-hand side.
In the second case, you end up with an object type, because nothing more specific can be inferred.
The rule here is that the type of the variable must have the generic type parameter satisfied. When you use const val: Generic = new Generic(); the type parameter is not satisfied - you can't ask for it to be inferred because you have decided to annotate the variable.
So the two allowable scenarios are:
To make your later example work, you have two options.
Option 1... if you really don't want to limit the type, go dynamic...
const val2 = new Generic<any>();
val2.prop.test = 1;
Or... Option 2... if you do want to limit the type, specify it.
const val2 = new Generic<{ test: number }>();
val2.prop.test = 1;
And finally, in many contexts, you don't need to specify the type argument as it can be inferred contextually.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With