3
votes

Typescript: Why is the result of the "typeof" operator correctly compared with literals and incorrectly with constants?

function foo(param: string) { }

const s = 'string';

function test(param: string | number) {
    if (typeof (param) == 'string') foo(param);  // ok
    if (typeof (param) == s) foo(param);         // error
}

Error message: "Argument of type 'string | number' is not assignable to parameter of type 'string'. Type 'number' is not assignable to type 'string'."

1

1 Answers

4
votes

TypeScript has special treatment for the use of typeof with the values "string", "number", "boolean", and "symbol", but only if they are literals.

The TypeScript spec says this about these type guards:

A type guard of the form typeof x === s, where s is a string literal with the value 'string', 'number', or 'boolean', when true, narrows the type of x to the given primitive type provided it is a subtype of the type of x, or, if the type of x is a union type, removes from the type of x all constituent types that aren't subtypes of the given primitive type, or when false, removes the primitive type from the type of x.

As shown here, the right-side of the equals can take on one of three (actually four, including 'symbol') values in order to use typeof as a type guard. Anything else (e.g. s) and it doesn't work.

Side note: there's no need to use parentheses around param in typeof (param).