Pesquisa de site

typescript - Tipo a partir dos valores de um objeto


Recentemente encontrei um problema de strings estritamente especificadas que podem ser passadas para uma função:

type TypesList = 'foo' | 'bar' | 'baz';

function send(type: TypesList): void {};

send('foo'); // Ok
send('error'); // Error

A função listada acima aceitará apenas um dos três valores de TypedList. O que eu basicamente estou tentando fazer é fornecer uma união de tipos a partir dos valores constantes para que, no caso de modificações futuras, eu não seja obrigado a atualizar o valor em todos os lugares.

const types = {
  foo: 'foo1',
  bar: 'bar2'
};

// Will check for "foo | bar" but I need "foo1 | bar2"
type TypesList = keyof typeof types; 

function send(type: TypesList): void {};
send(types.foo); // Invalid

Estou ciente do fato de que eu poderia simplesmente duplicar valores da constante ou verificar na função, mas estou procurando uma maneira mais limpa e robusta de lidar com isso.

Resposta #1

Enums de string estão disponíveis desde o TypeScript 2.4.0, o que parece ser exatamente o que você está procurando:

enum Type {
    foo = 'foo1',
    bar = 'bar2'
}

function send(type: Type): void {};
send(Type.foo);

O enum compila para um dicionário que mapeia o nome da chave para o valor, para uso em tempo de execução. Se você não precisar disso, apenas transforme-o em um const enum.

Além disso, atualmente não é possível definir um tipo como a união de todos os valores de um objeto, mesmo quando esse objeto é constante. Fazer uma união de strings seria a maneira usual de proceder, pois o principal objetivo de criar uma união de tipos literais de string é criar uma estrutura semelhante a um enum com string como seu tipo de dado. Felizmente, agora temos enums de string.

Como você estaria usando constantes bem definidas em todos os lugares, também há a chance de que você não precise de um tipo de união desse tipo. Nesse caso, você poderia colocar as constantes em um namespace e deixar que as funções recebam uma string comum:

namespace Types {
    export const foo = "foo1";
    export const bar = "bar2";
}

function send(type: string): void {};
send(Types.foo);