Pesquisa de site

O tipo genérico em uma classe TypeScript pode ser omitido


Usar Typescript com as opções padrão do compilador é bastante rígido sobre o que é permitido ou não - como valores nulos, atributos de classe não sendo inicializados no construtor. Mas quando se trata de genéricos, é possível definir um tipo genérico para uma classe e então criar uma nova classe sem especificar o tipo!

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

É possível considerar new Foo() como uma declaração errada?

Como especificado acima, estou usando as opções padrão do compilador, que não permitiriam adicionar um atributo extra a: T que nunca seria inicializado.

Resposta #1

Você não pode tornar a omissão de T no construtor um erro (bem, você provavelmente pode, mas precisaria de alguma mágica de tipo condicional e um construtor com pelo menos um argumento)

Você pode tornar a classe inutilizável se nenhum argumento for fornecido usando um valor padrão para o parâmetro de tipo. Um valor padrão de never fará o truque.

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 

Você também pode usar uma sobrecarga de construtor e tuplas em parâmetros rest para criar um construtor que dará um erro se você omitir o parâmetro de tipo (ou seja, o parâmetro de tipo é 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

Resposta #2

Além das outras respostas, gostaria de apontar que o TSLint possui uma regra especificamente contra este caso: no-inferred-empty-object-type

Ativar esta regra fará com que o TSLint reclame quando o TypeScript inferir {} como um genérico (similar à flag do compilador noImplicitAny, mas apenas para genéricos).

Isto parece ser exatamente o que você quer.