Short answer:

You can't use typeof at runtime to check for interface types, which only exist at compile time. Instead you can write a user-defined type guard function to check for such types:

const fruit = ["apple", "banana", "grape"] as const;
type Fruit = (typeof fruit)[number];
const isFruit = (x: any): x is Fruit => fruit.includes(x);

let myfruit = "pear";
if (isFruit(myfruit)) {
  console.log("My fruit is of type 'Fruit'");
}

Long answer follows:


You might be confused about the difference between values and types in TypeScript, especially as it relates to the typeof operator. As you may be aware, TypeScript adds a static type system to JavaScript, and that type system gets erased when the code is transpiled. The syntax of TypeScript is such that some expressions and statements refer to values that exist at runtime, while other expressions and statements refer to types that exist only at design/compile time. Values have types, but they are not types themselves. Importantly, there are some places in the code where the compiler will expect a value and interpret the expression it finds as a value if possible, and other places where the compiler will expect a type and interpret the expression it finds as a type if possible.

The typeof operator leads a double life. The expression typeof x always expects x to be a value, but typeof x itself could be a value or type depending on the context:

let bar = {a: 0};
let TypeofBar = typeof bar; // the value "object"
type TypeofBar = typeof bar; // the type {a: number}

The line let TypeofBar = typeof bar; will make it through to the JavaScript, and it will use the JavaScript typeof operator at runtime and produce a string. But type TypeofBar = typeof bar; is erased, and it is using the TypeScript type query operator to examine the static type that TypeScript has assigned to the value named bar.

In your code,

let myfruit = "pear";
if (typeof myfruit === "Fruit") { // "string" === "Fruit" ?!
    console.log("My fruit is of type 'Fruit'");
}

typeof myfruit is a value, not a type. So it's the JavaScript typeof operator, not the TypeScript type query operator. It will always return the value "string"; it will never be Fruit or "Fruit". You can't get the results of the TypeScript type query operator at runtime, because the type system is erased at runtime. You need to give up on the typeof operator.


What you can do is check the value of myfruit against the three known Fruit string literals... like, for example, this:

let myfruit = "pear";
if (myfruit === "apple" || myfruit === "banana" || myfruit === "grape") {
  console.log("My fruit is of type 'Fruit'");
}

Perfect, right? Okay, maybe that seems like a lot of redundant code. Here's a less redundant way to do it. First of all, define your Fruit type in terms of an existing array of literal values... TypeScript can infer types from values, but you can't generate values from types.

const fruit = ["apple", "banana", "grape"] as const;
export type Fruit = (typeof fruit)[number];

You can verify that Fruit is the same type as you defined yourself manually. Then, for the type test, you can use a user-defined type guard like this:

const isFruit = (x: any): x is Fruit => fruit.includes(x);

isFruit() is a function which checks if its argument is found in the fruit array, and if so, narrows the type of its argument to Fruit. Let's see it work:

let myfruit = "pear";
if (isFruit(myfruit)) {
  console.log("My fruit is of type 'Fruit'");
}

That type guard also lets the compiler know that inside the "then" clause of the if statement, that myfruit is a Fruit. Imagine if you had a function that only accepts Fruit, and a value that may or may not be a Fruit:

declare function acceptFruit(f: Fruit): void;
const myfruit = Math.random() < 0.5 ? "pear" : "banana";

You can't call the function directly:

acceptFruit(myfruit); // error, myfruit might be "pear"

But you can call it inside the "then" clause after checking it:

if (isFruit(myfruit)) {
  acceptFruit(myfruit); // okay, myfruit is known to be "banana"
}

Which is presumably why you want to check against your custom type in the first place. So that lets you do it.


To recap: you can't use typeof. You can compare against strings. You can do some type inference and a type guard to eliminate duplicated code and get control flow type analysis from the compiler.

Playground link to code

Answer from jcalz on Stack Overflow
Top answer
1 of 3
423

Short answer:

You can't use typeof at runtime to check for interface types, which only exist at compile time. Instead you can write a user-defined type guard function to check for such types:

const fruit = ["apple", "banana", "grape"] as const;
type Fruit = (typeof fruit)[number];
const isFruit = (x: any): x is Fruit => fruit.includes(x);

let myfruit = "pear";
if (isFruit(myfruit)) {
  console.log("My fruit is of type 'Fruit'");
}

Long answer follows:


You might be confused about the difference between values and types in TypeScript, especially as it relates to the typeof operator. As you may be aware, TypeScript adds a static type system to JavaScript, and that type system gets erased when the code is transpiled. The syntax of TypeScript is such that some expressions and statements refer to values that exist at runtime, while other expressions and statements refer to types that exist only at design/compile time. Values have types, but they are not types themselves. Importantly, there are some places in the code where the compiler will expect a value and interpret the expression it finds as a value if possible, and other places where the compiler will expect a type and interpret the expression it finds as a type if possible.

The typeof operator leads a double life. The expression typeof x always expects x to be a value, but typeof x itself could be a value or type depending on the context:

let bar = {a: 0};
let TypeofBar = typeof bar; // the value "object"
type TypeofBar = typeof bar; // the type {a: number}

The line let TypeofBar = typeof bar; will make it through to the JavaScript, and it will use the JavaScript typeof operator at runtime and produce a string. But type TypeofBar = typeof bar; is erased, and it is using the TypeScript type query operator to examine the static type that TypeScript has assigned to the value named bar.

In your code,

let myfruit = "pear";
if (typeof myfruit === "Fruit") { // "string" === "Fruit" ?!
    console.log("My fruit is of type 'Fruit'");
}

typeof myfruit is a value, not a type. So it's the JavaScript typeof operator, not the TypeScript type query operator. It will always return the value "string"; it will never be Fruit or "Fruit". You can't get the results of the TypeScript type query operator at runtime, because the type system is erased at runtime. You need to give up on the typeof operator.


What you can do is check the value of myfruit against the three known Fruit string literals... like, for example, this:

let myfruit = "pear";
if (myfruit === "apple" || myfruit === "banana" || myfruit === "grape") {
  console.log("My fruit is of type 'Fruit'");
}

Perfect, right? Okay, maybe that seems like a lot of redundant code. Here's a less redundant way to do it. First of all, define your Fruit type in terms of an existing array of literal values... TypeScript can infer types from values, but you can't generate values from types.

const fruit = ["apple", "banana", "grape"] as const;
export type Fruit = (typeof fruit)[number];

You can verify that Fruit is the same type as you defined yourself manually. Then, for the type test, you can use a user-defined type guard like this:

const isFruit = (x: any): x is Fruit => fruit.includes(x);

isFruit() is a function which checks if its argument is found in the fruit array, and if so, narrows the type of its argument to Fruit. Let's see it work:

let myfruit = "pear";
if (isFruit(myfruit)) {
  console.log("My fruit is of type 'Fruit'");
}

That type guard also lets the compiler know that inside the "then" clause of the if statement, that myfruit is a Fruit. Imagine if you had a function that only accepts Fruit, and a value that may or may not be a Fruit:

declare function acceptFruit(f: Fruit): void;
const myfruit = Math.random() < 0.5 ? "pear" : "banana";

You can't call the function directly:

acceptFruit(myfruit); // error, myfruit might be "pear"

But you can call it inside the "then" clause after checking it:

if (isFruit(myfruit)) {
  acceptFruit(myfruit); // okay, myfruit is known to be "banana"
}

Which is presumably why you want to check against your custom type in the first place. So that lets you do it.


To recap: you can't use typeof. You can compare against strings. You can do some type inference and a type guard to eliminate duplicated code and get control flow type analysis from the compiler.

Playground link to code

2 of 3
11

typeof in TS:

The typeof operator in TS can be used in 2 different contexts:

  1. In an expression/value context to return a string of its type. This is just the JavaScript typeof operator and will remain after a compile.
  2. In a type context to make the type similar to an existing expression/value. This is a TS construct to help us express ourselves more easily with certain types. This will be compiled away and not be present in the compiled JavaScript

Examples:

Expression/value context

const hi = 'hi';
const one = 1;
const obj = {};

console.log(typeof hi, typeof 1, typeof obj);
// [LOG]: "string",  "number",  "object"

Type context:

const obj1 = {foo: 1, bar: true};
const obj2 = {foo: 1, bar: ''};

// test has the type according to the structure of obj1
const test: typeof obj1 = {foo: 1, bar: true};
// typeof obj1 is the same as:
type sameAsTypeofObj1 = {foo: number, bar: string}


// test2 has the type according to the structure of obj1
const test2: typeof obj2 = {foo: 1, bar: true};
// In test2 we get a compile error since bar is not correct
// Since the type of obj2 is {foo: number, bar: string} we get the error:
// Type 'boolean' is not assignable to type 'string'

For your specific problem I think you should use user defined type guards. Here is an example:

type Fruit = "apple" | "banana" | "grape";

let myfruit = "apple";

// user defined type guard
function isFruit(fruit: string): fruit is Fruit {
  return ["apple", "banana", "grape"].indexOf(fruit) !== -1;
}

if (isFruit(myfruit)) {
    // if this condition passes 
    // then TS compiler knows that myfruit is of the Fruit type
    console.log(`${myfruit} is a Fruit.`}
}
🌐
DEV Community
dev.to › onureren › how-to-check-against-custom-types-in-typescript-hh2
How To Check Against Custom Types In TypeScript - DEV Community
July 3, 2023 - const fruit = ['apple', 'orange', ... and it is completely discarded in the run-time. This means you CANNOT use typeof keyword to check for custom types....
Discussions

Custom types
If you need type algebra, look at example type CustomType = { a?: string; b?: number; c?: boolean; } // get type of "b" property, type number (or number | undefined if tsconfig settings strong) type BType = CustomType["b"]; // Identity of more types (but not all) type SameType = T1 extends T2 ? T2 extends T1 ? T1 : never : never; // Checked if LookupType contains inside ScanType type ContainsType = SameType<{ [P in keyof ScanType] : SameType }[keyof ScanType], LookupType> extends LookupType ? true: false; // Example, ContainsString - to be type true type ContainsString = ContainsType // Example, ContainsObject - to be type false type ContainsObject = ContainsType Otherwise, explain more example, sorry. More on reddit.com
🌐 r/typescript
7
2
May 6, 2021
How do you check if some type matches your custom type?
Obviously zod's enum method has to get a mention here More on reddit.com
🌐 r/typescript
5
1
January 17, 2025
When should and shouldn't you create custom types? When is it overkill?
I think marking already existing types with a name that makes them more specific, is a good way to work with! Sure, multiple things could be `Record` but it's definitely more readable and organized to have a name for each. You could always apply the "branded type" pattern, if you want to mark certain types as the "this is definitely the one". This is a good article on branded types: https://egghead.io/blog/using-branded-types-in-typescript All in all, I'd say that whenever a type starts to become difficult to distinct from others (due to whichever reason in that scenario), that'd be a good time to create custom types. This definitely applies to larger projects. Imagine yourself being an outside developer who comes into your project, takes a look and sees `FormattedRow` instead of `Record`. Sure the type behind it is the same, but it's a lot more readable. More on reddit.com
🌐 r/typescript
29
20
September 4, 2024
How to create Type for massive nested object that change structure constantly?
Why is this object changing so much? Doesn't that constantly break your code? More on reddit.com
🌐 r/typescript
20
5
January 18, 2023
🌐
typescriptlang.org
typescriptlang.org › docs › handbook › 2 › types-from-types.html
TypeScript: Documentation - Creating Types from Types
Typeof Type Operator - Using the typeof operator to create new types · Indexed Access Types - Using Type['a'] syntax to access a subset of a type · Conditional Types - Types which act like if statements in the type system · Mapped Types - Creating types by mapping each property in an existing ...
🌐
TypeScript
typescriptlang.org › docs › handbook › 2 › typeof-types.html
TypeScript: Documentation - Typeof Type Operator
TypeScript adds a typeof operator you can use in a type context to refer to the type of a variable or property:
🌐
Reddit
reddit.com › r/typescript › custom types
r/typescript on Reddit: Custom types
May 6, 2021 -

Hey!

If I have a custom type, like:

export type CustomType = {a?: string;b?: number;c?: boolean;

}

Is there anyway to check if a string variable is in the type?

Something like a function belongsToCustomType("b"). And if so can i get the type of a (in this case number).

Sorry if it sounds confusing, i'm still new to typescript and don't know the terms very well.

Thank you!

Top answer
1 of 2
1
If you need type algebra, look at example type CustomType = { a?: string; b?: number; c?: boolean; } // get type of "b" property, type number (or number | undefined if tsconfig settings strong) type BType = CustomType["b"]; // Identity of more types (but not all) type SameType = T1 extends T2 ? T2 extends T1 ? T1 : never : never; // Checked if LookupType contains inside ScanType type ContainsType = SameType<{ [P in keyof ScanType] : SameType }[keyof ScanType], LookupType> extends LookupType ? true: false; // Example, ContainsString - to be type true type ContainsString = ContainsType // Example, ContainsObject - to be type false type ContainsObject = ContainsType Otherwise, explain more example, sorry.
2 of 2
1
I once needed to guarantee a runtime check on a type, so I built the type out of values. But this is heavy on the compiler, and I wouldn't recommend it with more than a few fields in a real-world project: const pairs = [["a", "string"], ["b", 0], ["c", "boolean"]] as const; // [key, valueType] type Pair = readonly [string, any]; type TypeStringToType = T extends "string" ? string : T extends "number" ? number : T extends "boolean" ? boolean : T extends "null" ? null : T extends "bigint" ? bigint : T extends "symbol" ? symbol : T // [["a", "string"], ["b", 0]] -> (["a", "string"] | ["b", 0]) type PairTupleToUnion = T[keyof T] extends infer TItem ? (TItem extends Pair ? TItem : never) : never; // ["a", "string"] -> { a: string } type PairsToKeyValue = T extends readonly [infer A, infer B] ? A extends string ? {[K in A]: TypeStringToType} : never : never // (X | Y) -> (X & Y) type UnionToIntersection = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never // convert a tuple of pairs into the type type PairsToType = UnionToIntersection>> // finally, construct custom type from runtime object type CustomType = PairsToType const a: CustomType = { a: "i'm a string", // type: string b: 0, // type: 0 c: false, // type : boolean } function belongsToType(value: string) { for (let [k,_] of pairs) { if (k === value) { return true } } return false } console.log(belongsToType("a")) // true console.log(belongsToType("e")) // false playground
🌐
DigitalOcean
digitalocean.com › community › tutorials › how-to-create-custom-types-in-typescript
How To Create Custom Types in TypeScript? | DigitalOcean
September 17, 2025 - In this section, you are going to create types that can be used to describe any object shape you need to use in your code. In TypeScript, the syntax for creating custom types is to use the type keyword followed by the type name and then an assignment to a {} block with the type properties.
🌐
Fjolt
fjolt.com › article › typescript-typeof-operator
How the typeof Operator works in TypeScript
July 2, 2022 - let x = 1234; // Custom type aNumber type aNumber = typeof x;
🌐
TutorialsPoint
tutorialspoint.com › typescript › typescript_creating_types_from_types.htm
TypeScript - Creating Types from Types
You can use the typeOf variable to extract the data type of the particular variable, object, function, etc., and use the data type for other variables. You can follow the syntax below to create custom types by using the typeof type operator.
Find elsewhere
🌐
Manning Publications
livebook.manning.com › book › typescript-quickly › chapter-2
Chapter 2. Basic and custom types · TypeScript Quickly
April 21, 2023 - Declaring variables with types, and using types in function declarations · Declaring type aliases with the type keyword · Declaring custom types with classes and interfaces
🌐
This Dot Labs
thisdot.co › blog › creating-custom-types-in-typescript-with-indexed-access-types-const
Creating Custom Types in TypeScript with Indexed Access Types, Const Assertions, and Satisfies - This Dot Labs
April 19, 2023 - TypeScript has widened the type from those literal values to the broader string type. It doesn't assume that these values can't be changed later on. But it did notice that every name in TOPPINGS was a string, so it decided that the string type was the safest bet. Here, you can see how it would widely interpret the type of any entry in TOPPINGS: ... type Toppings = typeof TOPPINGS[number]; // type Topping1 = { // name: string; // description: string; // price: number; // }[]
🌐
Reddit
reddit.com › r/typescript › how do you check if some type matches your custom type?
r/typescript on Reddit: How do you check if some type matches your custom type?
January 17, 2025 -

So, I have basically ran into this issue a lot where I have to set the value of a string but I want to allow only a few specific kind of strings and not allow others for which I use something like this

    type SupportedPlatformsType = "darwin" | "win32" | "linux";

Now the issue I run into is that how to check if a string matches this type without having to type basically every possible value that it can have

Using an array does seem like something I can do to avoid this issue but it just seems wrong to have to create an array every time I want a union Other methods I have tried also feel wrong in some way or the other Tell me what do you do?

🌐
Medium
medium.com › @rashmipatil24 › this-typescript-trick-will-blow-your-mind-extracting-custom-types-from-string-arrays-426d16394284
This TypeScript Trick Will Blow Your Mind: Extracting Custom Types from String Arrays | by Rashmi Patil | Medium
October 24, 2024 - This tells TypeScript to treat each element as a literal type, not just a general string. ... Next, you can extract a custom type from this array using typeof and number[]. This creates a union type from the string literals in the array.
🌐
Fjolt
fjolt.com › article › typescript-creating-custom-types
Creating Custom Types in Typescript
July 2, 2021 - Our example above looks like this ... identity: string } We can also define custom types using a much simpler syntax known as union types....
🌐
GeeksforGeeks
geeksforgeeks.org › typescript › typescript-custom-type-guards
TypeScript Custom Type Guards - GeeksforGeeks
July 23, 2025 - These are the approaches for TypeScript Custom Type Guards: Table of Content · Custom Type Guard for Discriminated Union · Custom Type Guard for Unknown Types · Using Type Guards in Array Filtering · Custom Type Guards with instanceof · Custom Type Guards with typeof ·
🌐
LogRocket
blog.logrocket.com › home › how to use type guards in typescript
How to use type guards in TypeScript - LogRocket Blog
June 4, 2024 - TypeScript uses built-in operators like typeof, instanceof, in, and is, which are used to determine if an object contains a property.
🌐
Scaler
scaler.com › home › topics › typescript › typescript typeof
TypeScript TypeOf - Scaler Topics
May 4, 2023 - By using this keyword, we can easily ... to create new basic types. If we define our custom types, then we can use typeof to copy the type of an existing ......
🌐
Reddit
reddit.com › r/typescript › when should and shouldn't you create custom types? when is it overkill?
r/typescript on Reddit: When should and shouldn't you create custom types? When is it overkill?
September 4, 2024 -

I'm really digging into typescript using it on a medium-sized Next js project with a ton of Node backend code. I want to do things the "right way" within type script but I often ask myself if I should be creating a custom type or not. For example, I am working with spreadsheet data from the Node xlsx library which is just of type Record<string, any> but I would like to keep track of what is specifically spreadsheet data and not some other Record<string, any> data so I made these types:

type FormattedRow = Record<string, any>;
type FormattedData = FormattedRow[]

Is this unnecessary, should I just use Record<string, any>? BTW I do have JSDoc strings, I just didn't paste them here

Also, when I make a type that is essentially just an array of another type like FormattedData above, when I hover over it, I don't get any useful information, just "FormattedRow[]" so then someone would have to open the custom types file to refer to it to see what FormattedRow is. Is that bad practice? I can't find any general guides online about when its a good idea to make a custom type and when it's not necessary.

Thanks!

Top answer
1 of 14
32
I think marking already existing types with a name that makes them more specific, is a good way to work with! Sure, multiple things could be `Record` but it's definitely more readable and organized to have a name for each. You could always apply the "branded type" pattern, if you want to mark certain types as the "this is definitely the one". This is a good article on branded types: https://egghead.io/blog/using-branded-types-in-typescript All in all, I'd say that whenever a type starts to become difficult to distinct from others (due to whichever reason in that scenario), that'd be a good time to create custom types. This definitely applies to larger projects. Imagine yourself being an outside developer who comes into your project, takes a look and sees `FormattedRow` instead of `Record`. Sure the type behind it is the same, but it's a lot more readable.
2 of 14
6
I mean it depends on who's going to be reading and editing the code in the future. There's not a hard and fast set of rules. What you do will depend a lot on your own experience. Ask yourself: Does extracting a custom type with its own name make the code easier to read or harder to read? How much jumping around are devs going to have to do to figure out the type? Sometimes when you hover over types TypeScript will only show the name of the type as it's type, not what the type evaluates to. Check to see if your type does that or not. If it does, devs will have to jump around more. Remember that TypeScript is structurally typed, not nominally typed. This means custom types are type aliases, not completely separate types. If two random custom types are both Record, they are interoperable with each other. Think about types in your codebase that have been extracted but are an absolute pain to work with. Or think about types that are repeated everywhere and are a pain to work with. Which feels worse? What are common characteristics of either that push you to do the opposite thing? Keep a note when a type annoys you and jot it down (physically or mentally). Then after a bit you'll be able to use the data you've got to make better decisions. Until you have your own experiences or your team's experiences to base decisions off of, the answer is really "either is fine". Cargo culting either way harms the quality of your code because it's not fixing your actual problems. This is like trying to apply all of the complex OOP advice you see on a greenfield project when really that OOP advice is for very complicated codebases with tens or hundreds of developers touching the same code. When you figure out what works for your team, then share it with the rest of us because we would all love to hear :)
🌐
GitConnected
levelup.gitconnected.com › user-defined-type-guards-in-typescript-fad639e4944f
User-defined Type Guards in Typescript | by Slawek Plamowski | Level Up Coding
January 8, 2020 - For example, it will narrow variable type to Cat if it’s not instanceof Dog: typeof is used when you need to distinguish between types number, string, boolean, bigint, object, function, symbol, and undefined.