Generický typ na triede v Typescripte môže byť vynechaný


Používanie Typescriptu s predvolenými možnosťami kompilátora je dosť prísne na to, čo je povolené alebo nie - napríklad nulové hodnoty, atribúty triedy, ktoré nie sú inicializované v konštruktore. Ale pokiaľ ide o generiká, je možné definovať generický typ pre triedu a potom vytvoriť novú triedu bez špecifikovania typu!

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

Je možné považovať new Foo() za nesprávny príkaz?

Ako je uvedené vyššie, používam predvolené možnosti kompilátora, ktoré by neumožnili pridať ďalší atribút a: T, ktorý by nikdy nebol inicializovaný.

Odpoveď č. 1

Nemôžete urobiť vynechanie T v konštruktore za chybu (no pravdepodobne môžete, ale potrebovali by ste nejakú podmienenú typovú mágiu a konštruktor s aspoň jedným argumentom)

Môžete spraviť triedu nepoužiteľnou, ak nie je zadaný žiadny argument, použitím predvoleného typu parametra. Predvolená hodnota never to urobí.

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 

Môžete tiež použiť preťaženie konštruktora a n-tice v zvyšných parametroch na vytvorenie konštruktora, ktorý dá chybu, ak vynecháte typový parameter (t. j. typový parameter je 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

Odpoveď č. 2

Okrem ostatných odpovedí by som rád poukázal na to, že TSLint má pravidlo špeciálne proti tomuto prípadu: no-inferred-empty-object-type

Povolením tohto pravidla spôsobíte, že TSLint bude upozorňovať, keď TypeScript odvodí {} ako generický typ (podobne ako prepínač kompilátora noImplicitAny, ale pre generiká).

Zdá sa, že toto je presne to, čo chceš.