in in Operator Narrowing

Last updated: 2026-10-22

in in Operator Narrowing

The in operator checks whether a property name exists on an object, and TypeScript narrows the union to the member that has that property. When union members share no discriminant field and are interfaces (so instanceof is unavailable), in is the handiest way to tell shapes apart.

GroupNarrowing & Assertions
KindOperator
Strict modeNot required

ℹ️ Info The editor transpiles TypeScript to JavaScript and runs it (no type checking); see each example's Output for type-check results.

📝 Syntax

interface Bird { fly: () => void }
interface Fish { swim: () => void }

function move(animal: Bird | Fish) {
  if ("swim" in animal) {
    animal.swim();   // narrowed to Fish
  } else {
    animal.fly();    // narrowed to Bird
  }
}

⚙️ Parameters

"key" in objAt runtime, checks whether key is a property of obj (including the prototype chain) and returns a boolean; TS also narrows obj to the union member containing that property

Returns:The in expression returns a boolean; narrowing happens at compile time and relies on property exclusivity between union members (having vs lacking a property).

▶ Example

Edit the code below and click Try it to see the transpiled result:

Output:

bubble...
it swims
flap!
it flies

(Runtime output after transpilation; animal.swim() type-checks because in narrowing has fixed animal as Fish — narrowing is guaranteed statically by tsc, at runtime there are only values)

💡 Notes

ℹ️ Info in tests for property existence, including inherited properties on the prototype chain. Plain objects have built-ins like toString and constructor, so check with your own business field names.
⚠️ Note in narrowing relies on exclusivity: if two members both have the same field (differing only in type), in helps little. Prefer a discriminated union with distinct literal values instead.
💡 Tip in is also the core syntax of mapped types ([K in keyof T]), but that is the type-level use; this entry covers the runtime operator plus compile-time narrowing.
More cases coming soon.

📚 Related tutorials

❓ FAQ

Q How do I choose between in narrowing and discriminated unions?
A If you can give each member a distinct literal field (type/status/kind), use a discriminated union — clearer and supports exhaustiveness checks. For third-party unions with no discriminant, distinguish structurally with in.
Q Can optional properties mislead in narrowing?
A Yes. An optional flag?: boolean may genuinely be absent, so flag in obj === false does not prove membership in the other branch. Confirm the property is required in one member and absent in the others before relying on in.
Q Does in still narrow correctly after delete removes a property?
A Yes — after delete obj.key the runtime in result is false and TS narrows to the member without that property. But note strict TS already makes deleting a required property a compile error, precisely to keep runtime and declared types in sync.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏