TypeScript-luokan geneerinen tyyppi voidaan jättää pois


Typescriptin käyttäminen oletuskääntäjäasetuksilla on melko tiukkaa sen suhteen, mitä sallitaan tai ei - kuten null-arvot, luokan attribuutit, joita ei aloiteta konstruktorissa. Mutta kun kyse on genericsistä, on mahdollista määritellä geneerinen tyyppi luokalle ja sitten luoda uusi luokka ilman, että tyyppiä määritellään!

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

Onko mahdollista pitää new Foo() virheellisenä lauseena?

Kuten yllä on todettu, käytän oletuskääntäjäasetuksia, jotka eivät salli lisätä ylimääräistä attribuuttia a: T, jota ei koskaan alustettaisi.

Vastaus #1

Et voi tehdä T-merkin poisjättämisestä konstruktorissa virhettä (no, todennäköisesti voit, mutta sinun täytyisi käyttää jonkinlaista ehdollista tyyppimaagia ja konstruktorilla täytyy olla vähintään yksi argumentti)

Voit tehdä luokasta käyttökelvottoman, jos mitään argumenttia ei anneta, käyttämällä oletusta tyyppiparametrille. never-oletus hoitaa asian.

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 

Voit myös käyttää konstruktorin ylikuormitusta ja joukkioita (tuples) levittämisparametreissa tehdäksesi konstruktorin, joka antaa virheen, jos jätät tyyppiparametrin pois (eli tyyppiparametri on 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

Vastaus #2

Muiden vastausten lisäksi haluaisin huomauttaa, että TSLintilla on sääntö, joka koskee nimenomaan tätä tapausta: no-inferred-empty-object-type

Tämän säännön ottaminen käyttöön saa TSLintin valittamaan, kun TypeScript päättelee {} generyyliksi (vastaavasti kuin noImplicitAny -kääntäjävalitsin, mutta vain generyyleille).

Tämä näyttää olevan juuri sitä, mitä haluat.