ค้นหาเว็บไซต์

javascript - ตัวตรวจชนิดข้อมูลไม่ทำงานอย่างถูกต้องแม้ว่าชนิดเงื่อนไขที่เทียบเท่าจะทำงาน


พิจารณาการประกาศและการใช้งานประเภทตามเงื่อนไขต่อไปนี้:

type IsIterable<T> = T extends Iterable<infer X> ? Iterable<X> : never
export function isIterableAny(val: any): val is IsIterable<typeof val> {
    return hasValue(val) && typeof (val as any)[Symbol.iterator] === "function"
}
export function isIterableUnknown(val: unknown): val is IsIterable<typeof val> {
    return hasValue(val) && typeof (val as any)[Symbol.iterator] === "function"
}

const num = 1 // 1
const arr = [1, 2, 3] // number[]

type TestNumber = IsIterable<typeof num> // never
type TestArray = IsIterable<typeof arr> // Iterable<number>

if (isIterableAny(num)) { num /* 1 & Iterable<unknown> */ }
if (isIterableUnknown(num)) { num /* never */ }

if (isIterableAny(arr)) { arr /* number[] */ }
if (isIterableUnknown(arr)) { arr /* never */ }

ทำไมผลลัพธ์ของตัวตรวจสอบประเภท isIterable จึงไม่ตรงกับผลลัพธ์ของประเภททดสอบ แม้ว่าทั้งหมดจะใช้ประเภทเงื่อนไขเดียวกัน IsIterable?

ฉันสังเกตว่ารุ่นสองแบบของตัวป้องกันชนิดทำงานและล้มเหลวในวิธีที่แตกต่าง/เสริมกัน แต่แน่นอนว่าฉันต้องการตัวป้องกันชนิดเดียวที่ทำงานได้กับข้อมูลนำเข้าทั้งหมด

คำตอบ #1

type IsIterable<T> = T extends Iterable<infer X> ? Iterable<X> : never;

โดยการใช้สิ่งนี้ คุณกำลังละทิ้งข้อมูลประเภททั้งหมดที่เกี่ยวข้องกับ T นอกจากคุณสมบัติที่สามารถวนซ้ำได้ของมัน แทนที่นั้น คุณเพียงแค่ต้องยกเว้นสมาชิกทั้งหมดจากสหสัมพันธ์ T ที่ไม่ได้สืบทอดประเภท Iterable:

สนามเด็กเล่น TS

export function isIterable<T>(value: T): value is T extends Iterable<any> ? T : never  {
  try { return typeof (value as any)[Symbol.iterator] === 'function'; }
  catch { return false; }
}

declare const num: 1;
isIterable(num) && num; // never

declare const arr: number[];
isIterable(arr) && arr; // number[]

declare const union: null | string[] | Generator<bigint> | Iterator<number> | Record<'a' | 'b', boolean>;
isIterable(union) && union; // string[] | Generator<bigint>