gabriel
gabriel

Reputation: 178

Generic type on Typescript class can be omitted

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

Answers (3)

Madara&#39;s Ghost
Madara&#39;s Ghost

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

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

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

Erik Philips
Erik Philips

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 Tis 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

Related Questions