Reputation: 178
Using Typescript with default compiler options is pretty strict on what is allowed or not - like null values, class attributes not being initiated in constructor. But when it comes to generics, it is possible to define a generic type for a class and then create a new class without specifying the type!
class Foo<T> {
bar(item: T): void {
console.log('typeof T: ', typeof item)
}
}
const foo1 = new Foo<string>() // T specified
foo1.bar('hello')
foo1.bar(6) // error TS2345: Argument of type '6' is not assignable to parameter of type 'string'
const foo2 = new Foo() // T missing
foo2.bar('hello')
foo2.bar(6) // works with no complaint
Is that possible to consider new Foo()
as being a wrong statement?
As specified above I am using the default compiler options which would not allow to add an extra attribute a: T
which would never be initialised.
Upvotes: 1
Views: 1179
Reputation: 175088
In addition to the other answers, I'd like to point out that TSLint has a rule specifically against this case: no-inferred-empty-object-type
Enabling this rule will make TSLint complain about when TypeScript infers {}
as a generic (similar to the noImplicitAny
compiler flag, only for generics).
This seems to be exactly what you want.
Upvotes: 2
Reputation: 250336
You can't make the omission of T
on the constructor an error (well you probably can but you would need some conditional type magic and a constructor with at least one argument)
You can make the class unusable if no argument is supplied by using a default for the type parameter. A default of never
will do the trick.
class Foo<T = never> {
bar(item: T): void {
console.log('typeof T: ', typeof item)
}
}
const foo1 = new Foo<string>() // T specified
foo1.bar('hello')
foo1.bar(6) // error TS2345: Argument of type '6' is not assignable to parameter of type 'string'
const foo2 = new Foo() // T missing
foo2.bar('hello') // err
foo2.bar(6) // err
You can also use a constructor overload and tuples in rest parameters to make a constructor that will give an error if you omit the type parameter (ie the type parameter is never
)
class Foo<T = never> {
constructor(...a: T extends never ? ['No T was specified']:[])
constructor() {
}
bar(item: T): void {
console.log('typeof T: ', typeof item)
}
}
const foo1 = new Foo<string>() // T specified
foo1.bar('hello')
foo1.bar(6) // error TS2345: Argument of type '6' is not assignable to parameter of type 'string'
const foo2 = new Foo() // T missing, error!
foo2.bar('hello')//err
foo2.bar(6) //err
Upvotes: 5
Reputation: 54636
it is possible to define a generic type for a class and then create a new class without specifying the type!
Of course, it's called type argument inference:
that is, we want the compiler to set the value of T for us automatically based on the type of the argument we pass in:
so
Is that possible to consider new Foo() as being a wrong statement?
No, because the compiler is probably either setting T
as any
or string | number
based on the code you've already written. So T
is completely valid.
taking a look at your code:
const foo2 = new Foo() // T missing
foo2.bar('hello')
foo2.bar(6) // works with no complaint
Why would it complain, the compiler has to infer a type so it's looking at all usages to infer a valid type. After all what is the difference between the previous code and the following:
const foo3 = new Foo() // T missing
foo3.bar(6)
foo3.bar('hello') // works with no complaint
Only the order of operations. Why should the compiler assume that only the first usage of a method determine the type of T
? What if it was the following:
const foo3 = new Foo() // T missing
if (someValueOrBooleanReturningFunction) {
foo3.bar(6)
}
foo3.bar('hello') // works with no complaint
Then what? Maybe it could or maybe it couldn't determine the value of the if
, but either way since the a type isn't specified, the compiler has to determine what T
is.
Upvotes: 1