typeof and instanceof Guards
Narrowing is TypeScript’s ability to refine a type within a conditional branch. When you check what kind of value you have, TypeScript updates its understanding of the type for the rest of that branch. The two most fundamental narrowing tools are typeof for primitives and instanceof for class instances.
typeof guards
Section titled “typeof guards”typeof returns a string describing the runtime type of a value. TypeScript understands every possible return value and narrows accordingly:
function display(value: string | number): string { if (typeof value === 'string') { return value.toUpperCase(); // TypeScript knows: value is string } return value.toFixed(2); // TypeScript knows: value is number}Inside the typeof === 'string' branch, value is narrowed to string. In the else branch (or after the if), value is narrowed to number — the remaining possibility.
The values typeof can return and the types they narrow to:
typeof result | Narrows to |
|---|---|
'string' | string |
'number' | number |
'boolean' | boolean |
'undefined' | undefined |
'function' | Function |
'object' | object | null (note: typeof null === 'object') |
'symbol' | symbol |
'bigint' | bigint |
The null caveat matters: typeof null === 'object', so a typeof value === 'object' check does not rule out null. Always pair it with a null check when narrowing to a non-null object.
instanceof guards
Section titled “instanceof guards”instanceof checks whether an object was created by a particular constructor. TypeScript narrows to the class type:
class QuizError extends Error { constructor(public code: number, message: string) { super(message); }}
function handleError(err: Error): void { if (err instanceof QuizError) { console.log('Quiz error code:', err.code); // TypeScript knows: err is QuizError } else { console.log('General error:', err.message); // TypeScript knows: err is Error }}Inside the instanceof QuizError branch, err is narrowed to QuizError, giving access to err.code. Outside the branch, it remains Error.
Catching errors safely
Section titled “Catching errors safely”In TypeScript with strict: true, caught errors in a try/catch block are typed as unknown — because anything can be thrown, not just Error instances:
try { const data = await fetchJson<ApiResponse>(url);} catch (err) { // err: unknown — could be an Error, a string, or anything else console.log(err.message); // Error: 'err' is of type 'unknown'}Use instanceof Error to narrow before accessing Error properties:
try { const data = await fetchJson<ApiResponse>(url);} catch (err) { const message = err instanceof Error ? err.message : 'Unknown error'; alert(`Could not load questions: ${message}`);}This pattern appears in every catch block in AceIt’s main.ts. The instanceof Error check is not just a TypeScript formality — it handles the real possibility that something other than an Error was thrown.
Narrowing optional values
Section titled “Narrowing optional values”typeof is also the natural tool for narrowing optional parameters — which are T | undefined:
function buildUrl(amount: number, difficulty?: Difficulty): string { let url = `https://opentdb.com/api.php?amount=${amount}`; if (typeof difficulty !== 'undefined') { url += `&difficulty=${difficulty}`; // TypeScript knows: difficulty is Difficulty } return url;}The truthiness check (if (difficulty)) is more concise and equally valid for non-empty strings, but typeof !== 'undefined' is explicit and avoids accidentally treating empty strings or 0 as absent.
Exercise
Section titled “Exercise”In a scratch file narrowing-practice.ts:
- Write a function
safeCatch(fn: () => void): stringthat callsfninside atry/catchand returnserr instanceof Error ? err.message : 'Unknown error'. Call it with a function that throws a string and one that throws anError— confirm both are handled. - Write
describeValue(value: string | number | boolean): stringthat usestypeofto return a description like'string: AceIt','number: 10', or'boolean: true'. - Try accessing
.messageon the caught value without theinstanceofguard — read the error TypeScript reports.
You will apply the instanceof Error pattern to every catch block in main.ts when you write it in Module 07.
typeofnarrows primitive types:'string','number','boolean','undefined','function'.typeof null === 'object'— pair an object check with a null check when needed.instanceofnarrows class instances — the value is refined to the specific class type inside the branch.- Caught errors are
unknownin strict TypeScript — always narrow withinstanceof Errorbefore accessing error properties. - Truthiness checks narrow out
undefined,null,0, and''— usetypeof !== 'undefined'when you need precision.