Kodokon kodokon.com

Narrowing: typeof, in, instanceof, और type guards

आप टाइप narrowing की व्यवस्थाओं में महारत हासिल करते हैं और unions को अधिक भरोसेमंद बनाने के लिए अपने खुद के predicates लिखते हैं।

10 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

Narrowing वह व्यवस्था है जिसके ज़रिए TypeScript नियंत्रण-प्रवाह (control flow) का विश्लेषण करके किसी व्यापक टाइप (एक union, unknown) को अधिक सटीक टाइप में परिष्कृत करता है। आप इसे बिना सोचे-समझे पहले से ही इस्तेमाल करते हैं: किसी typed मान पर लगाया गया हर if एक विश्लेषण को सक्रिय करता है। कंपाइलर तीन प्रकार के अंतर्निर्मित guards को ट्रैक करता है: primitives के लिए typeof, class instances के लिए instanceof, और किसी property की मौजूदगी के लिए in। इन्हें सही ढंग से चुनना आपको as के साथ की जाने वाली लापरवाह casts से बचाता है, जो सत्यापन का मार्गदर्शन करने के बजाय उसे बंद कर देती हैं।

TYPESCRIPT
function formatValue(
  value: string | number | Date
): string {
  if (typeof value === "string") {
    return value.trim();
  }
  if (value instanceof Date) {
    return value.toISOString();
  }
  return value.toFixed(2);
}
हर शाखा टाइप को narrow करती है; आखिरी शाखा केवल number ही हो सकती है।

in ऑपरेटर तब अमूल्य होता है जब आप संरचनात्मक रूप से भिन्न ऐसे objects को संभालते हैं जिनमें कोई discriminant फ़ील्ड नहीं होती। लेकिन सावधान रहें: in किसी key की मौजूदगी की जाँच करता है, जिसमें विरासत में मिली (inherited) keys भी शामिल हैं, न कि उसके टाइप की। बाहरी डेटा (APIs, JSON) पर, यह आपकी घोषणाओं के अनुसार narrow करता है, न कि रनटाइम पर वास्तव में जो मौजूद है उसके अनुसार। एक क्लासिक वास्तविक-जगत का जाल: typeof value === "object" की जाँच null को गुज़र जाने देती है, क्योंकि JavaScript में typeof null का मान "object" होता है। हमेशा एक स्पष्ट null जाँच जोड़ें।

TYPESCRIPT
interface Cat { meow(): void }
interface Dog { bark(): void }

function speak(animal: Cat | Dog): void {
  if ("meow" in animal) {
    animal.meow();
  } else {
    animal.bark();
  }
}
in ऑपरेटर के साथ property की मौजूदगी द्वारा narrowing।

जब guard का तर्क पुनः प्रयोग करने योग्य हो जाए या flow विश्लेषण के लिए बहुत जटिल हो जाए, तो एक custom type guard लिखें: एक ऐसा function जिसका return टाइप value is T के रूप में annotate किया गया हो। तब कंपाइलर आपके implementation पर भरोसा करता है। यह एक अनुबंध है: यदि आपकी जाँच अधूरी है, तो आप रनटाइम errors को फिर से ले आते हैं जिन्हें टाइप सिस्टम कभी नहीं देख पाएगा। TypeScript 5.5 से, filter जैसे callbacks में सरल predicates अपने-आप infer हो जाते हैं, लेकिन exported functions के लिए स्पष्ट annotation अब भी मानक है।

TYPESCRIPT
interface User { id: string; email: string }

function isUser(value: unknown): value is User {
  return (
    typeof value === "object" &&
    value !== null &&
    "id" in value &&
    typeof value.id === "string" &&
    "email" in value &&
    typeof value.email === "string"
  );
}

const raw: unknown =
  JSON.parse('{"id":"1","email":"a@b.c"}');
if (isUser(raw)) {
  console.log(raw.email.toLowerCase());
}
एक predicate जो उपयोग से पहले बाहरी डेटा को सत्यापित करता है।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. किसी गैर-null object में narrow करने के लिए typeof value === "object" की जाँच पर्याप्त क्यों नहीं है?
    • क्योंकि typeof बड़े अक्षर के साथ "Object" लौटाता है
    • क्योंकि JavaScript में typeof null भी "object" होता है
    • क्योंकि typeof unions पर काम नहीं करता
    • क्योंकि arrays "array" लौटाते हैं
  2. कौन-सा return annotation किसी function को custom type guard में बदल देता है?
    • boolean
    • value as User
    • value is User
    • asserts value
  3. एक ख़राब तरीके से लिखे गए custom type guard का मुख्य जोखिम क्या है?
    • function के अंदर एक compile-time error
    • एक झूठा narrowing: कंपाइलर एक ऐसे टाइप पर विश्वास करता है जो रनटाइम पर ग़लत है
    • compilation के प्रदर्शन में हानि