O typeof significa duas coisas no TypeScript. Em uma expressão, é o operador do JavaScript: roda, retorna uma string como "string" ou "number", e o TypeScript faz o narrowing da variável quando você compara essa string. Em uma posição de tipo, é uma type query: typeof config dá o tipo estático da variável config.
A última linha mostra a diferença: typeof custom dentro de um console.log é o operador de tempo de execução e imprime object, enquanto typeof defaults depois de type Options = é a query de tempo de compilação e nunca chega ao JavaScript.
Os dois significados em resumo
typeof de tempo de execução (JavaScript) | typeof como type query (TypeScript) | |
|---|---|---|
| Onde aparece | Em qualquer expressão: if, return, console.log(...) | Em posições de tipo: depois de :, em type X = ..., dentro de <...> |
| Exemplo | typeof x === "string" | let y: typeof x; |
| Avaliado | Quando o código roda | Pelo compilador, e depois apagado |
| Resultado | Uma de oito strings | Um tipo |
| Serve para | Verificar um valor e estreitar o tipo dele | Derivar um tipo a partir de um valor |
O TypeScript os distingue pela posição, então a mesma palavra-chave nunca significa as duas coisas ao mesmo tempo.
Resultados do typeof em tempo de execução
O operador de tempo de execução retorna uma de oito strings. Rode isto para ver o que valores comuns dão:
| Valor | Resultado do typeof | Observação |
|---|---|---|
"hi", template strings | "string" | |
42, 3.14, NaN, Infinity | "number" | NaN é um número; verifique com Number.isNaN |
10n | "bigint" | |
true, false | "boolean" | |
undefined | "undefined" | |
Symbol("id") | "symbol" | |
| funções, arrow functions, classes | "function" | uma classe é uma função construtora |
null | "object" | um bug histórico do JavaScript |
{}, [], new Date(), new Map() | "object" | arrays são objetos: use Array.isArray |
new String("x"), new Number(1) | "object" | objetos wrapper, evite-os |
Em JavaScript puro, typeof em uma variável que nunca foi declarada retorna "undefined" em vez de lançar erro. No TypeScript esse código não compila: Cannot find name 'notDeclared'. (TS2304).
Narrowing com typeof
Uma comparação com typeof em um if, um switch, um operador ternário ou depois de && estreita a variável em cada ramo. É o jeito padrão de lidar com uma union de primitivos, e a primeira verificação a usar em um unknown.
typeof x !== "string" estreita no sentido contrário, para tudo exceto string. E o compilador conhece as oito strings possíveis, então um erro de digitação na comparação é erro de compilação, e não uma verificação que é sempre falsa em silêncio:
O compilador mostra index.ts(3,7): error TS2367: This comparison appears to be unintentional because the types '"bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined"' and '"strnig"' have no overlap. Em JavaScript puro, o erro de digitação mandaria em silêncio toda string pelo caminho do number. Corrija a grafia e ele imprime text.
A armadilha do typeof null
Como typeof null é "object", o TypeScript estreita typeof x === "object" para object | null, e não para object. Adicione uma verificação de null, senão ler uma propriedade é erro (TS18047 'x' is possibly 'null'.).
O typeof não distingue arrays, datas, maps e objetos simples: todos são "object". Use Array.isArray(x) para arrays e x instanceof Date para instâncias de classes. Nem o typeof nem nada mais consegue verificar em tempo de execução se um valor corresponde a uma interface, porque as interfaces são apagadas; para isso, escreva uma função type guard que verifica as propriedades.
typeof em posições de tipo
Em um tipo, typeof someVariable copia o tipo que o TypeScript inferiu (ou que você declarou) para aquela variável. Isso poupa escrever à mão um tipo para um valor que já existe e mantém os dois sincronizados.
const theme = {
primary: "#3178c6",
spacing: 8,
dark: false,
};
type Theme = typeof theme;
// { primary: string; spacing: number; dark: boolean }
function withSpacing(t: Theme, factor: number): Theme {
return { ...t, spacing: t.spacing * factor };
}
console.log(withSpacing(theme, 2)); // { primary: '#3178c6', spacing: 16, dark: false }
let userName = "Ada";
const fixed = "Ada";
type A = typeof userName; // string
type B = typeof fixed; // "Ada" (a const keeps its literal type)
O resultado depende de como a variável foi declarada: um let ou uma propriedade de objeto é alargado (string, number), um primitivo const mantém o literal type. Adicione as const a um objeto ou array e o typeof dá literal types readonly em todos os níveis. O typeof também aceita acesso a propriedades, typeof theme.spacing é number, mas não expressões arbitrárias: type T = typeof getTheme(); é um erro de sintaxe (';' expected., TS1005). Para o tipo que uma chamada retorna, use ReturnType<typeof getTheme>.
keyof typeof: as chaves de um objeto como tipo
keyof typeof obj é a combinação mais comum. O typeof transforma o objeto em um tipo, o keyof pega as chaves dele, e você obtém uma union dos nomes das propriedades que continua correta quando o objeto muda.
O mesmo truque em um array constante dá uma union dos valores dele: com const roles = ["admin", "editor"] as const, o tipo (typeof roles)[number] é "admin" | "editor". Sem as const seria só string. A página sobre keyof trata do keyof sozinho.
ReturnType<typeof fn> e typeof em classes
Utility types como ReturnType e Parameters recebem um tipo de função. O nome de uma função é um valor, então passe-o antes pelo typeof:
Escrever ReturnType<createUser> falha com 'createUser' refers to a value, but is being used as a type here. Did you mean 'typeof createUser'? (TS2749), que é o compilador pedindo exatamente isso.
Classes são o único caso em que um nome é ao mesmo tempo valor e tipo. User como tipo significa uma instância da classe; typeof User significa a própria classe, o construtor, incluindo os membros estáticos:
class Point {
static origin = new Point(0, 0);
constructor(public x: number, public y: number) {}
}
const p: Point = new Point(1, 2); // an instance
const Ctor: typeof Point = Point; // the class (constructor + statics)
const q = new Ctor(3, 4); // q: Point
type Instance = InstanceType<typeof Point>; // Point
typeof vs instanceof vs type guard
| Verificação | Funciona com | Exemplo |
|---|---|---|
typeof | primitivos, funções, "é um objeto?" | typeof x === "number" |
Array.isArray | arrays | Array.isArray(x) |
instanceof | instâncias de classes (Date, Error, suas classes) | x instanceof Date |
in | unions de objetos, por propriedade | "email" in x |
| Função type guard | interfaces, type aliases, qualquer outra coisa | isUser(x) com x is User |
Escolha a ferramenta mais específica que servir. Para instâncias de classes, veja instanceof; para formatos descritos por uma interface, uma função type guard é a única verificação de tempo de execução disponível.
Perguntas frequentes
O que o typeof faz no TypeScript?
Duas coisas diferentes, dependendo de onde aparece. Em uma expressão, typeof x é o operador do JavaScript: ele roda e retorna uma string como "string" ou "object", e o TypeScript faz o narrowing de x quando você compara essa string. Em uma anotação de tipo, typeof x é uma type query: é avaliada pelo compilador, dá o tipo estático da variável x e desaparece do JavaScript gerado.
O que é keyof typeof no TypeScript?
keyof typeof obj dá a union dos nomes das propriedades de um objeto como literal types de string. typeof obj transforma o valor do objeto no tipo dele, e keyof pega as chaves desse tipo. Para const colors = { red: "#f00", blue: "#00f" }, keyof typeof colors é "red" | "blue".
Por que typeof null retorna "object"?
É um bug da primeira versão do JavaScript que não pode mais ser corrigido sem quebrar a web. O TypeScript o modela: depois de typeof x === "object", x é estreitado para object | null, então você também precisa verificar x !== null antes de ler propriedades.
Como verificar se um valor é um array no TypeScript?
Use Array.isArray(value), que estreita para um tipo array. O typeof não consegue: typeof [] é "object", igual a objetos simples e a null.
Qual é a diferença entre typeof e instanceof no TypeScript?
O typeof verifica a categoria primitiva de um valor ("string", "number", "function", "object"...). O instanceof verifica se um objeto foi criado por uma classe ou construtor específico, como Date ou a sua própria class User. Use typeof para primitivos e instanceof para instâncias de classes; nenhum dos dois consegue verificar uma interface ou um type alias, que não existem em tempo de execução.