Narrow Types com `const` modifier
Podemos limitar a tipagem de TS para receber tipos mais estreitos ("narrow types"). Por exemplo, como essa variável é uma const
, o TS já declara ela do tipo literal ao invés de um tipo abrangente como string
const example = "hello world";
// ^? const example: "hello world"
Só que quando a gente vai passando valores pra lá e pra cá, alguns desses tipos podem acabar ficando mais abrangentes, o que geralmente não é desejado. Por exemplo, se eu passar para uma função que recebe e retorna string, o tipo mais estreito vai se perder no meio do caminho.
const returnString = (s: string) => s;
const example = returnString('Hello world');
// ^? const example: string
Com a ajuda de generics, podemos manter o tipo mais estreito
const returnString = <TString extends string>(s: TString) => s;
const example = returnString('Hello world');
// ^? const example: 'Hello world'
Isso não funciona com tipagens mais complexas. Se eu tentar fazer isso com um array de strings, o array é mutável por padrão, e não tem como TS perceber isso de antemão.
const returnArray = <TArray extends string[]>(arr: TArray) => arr;
const example = returnArray(['alice', 'bob', 'charles']);
// ^? const example: string[]
Nesses casos, era costume de desenvolvimento colocar um as const
depois do valor. Isso anuncia pro TS que esse array não é mutável e ele consegue retornar a tipagem original.
const example = returnArray(['alice', 'bob', 'charles'] as const);
// ^? const example: ['alice', 'bob', 'charles']
Mas isso não é o suficiente. Quem vai chamar a função precisa lembrar de colocar as const
, e isso torna o código refém de erros humanos. Também, já que toda vez que função for executada precisa passa as const
, o código fica mais verboso, ainda mais se a função é chamada muitas vezes pelo projeto. Essas falhas ficam bem pronunciadas para bibliotecas, já que o criador da API tem que educar os usuários da biblioteca a usar o as const
.
No TS v5.0, chega então o modificador const
de tipos genéricos
const returnArray = <const TArray extends string[]>(arr: TArray) => arr;
const example = returnArray(['alice', 'bob', 'charles']);
// ^? const example: ['alice', 'bob', 'charles']
Com o modificador const
antes da declaração do parâmetro TArray
, a função usa o valor que é passado para ela como se o argumento utilizasse as const
.