1use crate::instantiate::{TypeSubstitution, instantiate_type};
7use crate::subtype::TypeResolver;
8use crate::types::{
9 IntrinsicKind, LiteralValue, MappedModifier, MappedTypeId, ObjectShape, ObjectShapeId,
10 PropertyInfo, SymbolRef, TupleElement, TupleListId, TypeData, TypeId, TypeListId,
11 TypeParamInfo,
12};
13use crate::utils;
14use crate::visitor::{
15 TypeVisitor, array_element_type, literal_number, literal_string, tuple_list_id, union_list_id,
16};
17use crate::{ApparentMemberKind, TypeDatabase};
18
19use super::super::evaluate::{
20 ARRAY_METHODS_RETURN_ANY, ARRAY_METHODS_RETURN_BOOLEAN, ARRAY_METHODS_RETURN_NUMBER,
21 ARRAY_METHODS_RETURN_STRING, ARRAY_METHODS_RETURN_VOID, TypeEvaluator,
22};
23use super::apparent::make_apparent_method_type;
24
25fn is_member(name: &str, list: &[&str]) -> bool {
26 list.contains(&name)
27}
28
29pub(crate) fn get_array_member_kind(name: &str) -> Option<ApparentMemberKind> {
32 if name == "length" {
33 return Some(ApparentMemberKind::Value(TypeId::NUMBER));
34 }
35 if is_member(name, ARRAY_METHODS_RETURN_ANY) {
36 return Some(ApparentMemberKind::Method(TypeId::ANY));
37 }
38 if is_member(name, ARRAY_METHODS_RETURN_BOOLEAN) {
39 return Some(ApparentMemberKind::Method(TypeId::BOOLEAN));
40 }
41 if is_member(name, ARRAY_METHODS_RETURN_NUMBER) {
42 return Some(ApparentMemberKind::Method(TypeId::NUMBER));
43 }
44 if is_member(name, ARRAY_METHODS_RETURN_VOID) {
45 return Some(ApparentMemberKind::Method(TypeId::VOID));
46 }
47 if is_member(name, ARRAY_METHODS_RETURN_STRING) {
48 return Some(ApparentMemberKind::Method(TypeId::STRING));
49 }
50 None
51}
52
53struct IndexAccessVisitor<'a, 'b, R: TypeResolver> {
54 evaluator: &'b mut TypeEvaluator<'a, R>,
55 object_type: TypeId,
56 index_type: TypeId,
57}
58
59impl<'a, 'b, R: TypeResolver> IndexAccessVisitor<'a, 'b, R> {
60 fn evaluate_apparent_primitive(&mut self, kind: IntrinsicKind) -> Option<TypeId> {
61 match kind {
62 IntrinsicKind::String
63 | IntrinsicKind::Number
64 | IntrinsicKind::Boolean
65 | IntrinsicKind::Bigint
66 | IntrinsicKind::Symbol => {
67 let shape = self.evaluator.apparent_primitive_shape(kind);
68 Some(
69 self.evaluator
70 .evaluate_object_with_index(&shape, self.index_type),
71 )
72 }
73 _ => None,
74 }
75 }
76
77 fn is_generic_index(&self) -> bool {
83 let key = match self.evaluator.interner().lookup(self.index_type) {
84 Some(k) => k,
85 None => return false,
86 };
87
88 matches!(
89 key,
90 TypeData::TypeParameter(_)
91 | TypeData::Infer(_)
92 | TypeData::KeyOf(_)
93 | TypeData::IndexAccess(_, _)
94 | TypeData::Conditional(_)
95 | TypeData::TemplateLiteral(_) | TypeData::Intersection(_)
97 )
98 }
99
100 fn evaluate_type_param(&mut self, param: &TypeParamInfo) -> Option<TypeId> {
101 if let Some(constraint) = param.constraint {
102 if constraint == self.object_type {
103 Some(
104 self.evaluator
105 .interner()
106 .index_access(self.object_type, self.index_type),
107 )
108 } else {
109 Some(
110 self.evaluator
111 .recurse_index_access(constraint, self.index_type),
112 )
113 }
114 } else {
115 Some(
116 self.evaluator
117 .interner()
118 .index_access(self.object_type, self.index_type),
119 )
120 }
121 }
122}
123
124impl<'a, 'b, R: TypeResolver> TypeVisitor for IndexAccessVisitor<'a, 'b, R> {
125 type Output = Option<TypeId>;
126
127 fn visit_intrinsic(&mut self, kind: IntrinsicKind) -> Self::Output {
128 self.evaluate_apparent_primitive(kind)
129 }
130
131 fn visit_literal(&mut self, value: &LiteralValue) -> Self::Output {
132 self.evaluator
133 .apparent_literal_kind(value)
134 .and_then(|kind| self.evaluate_apparent_primitive(kind))
135 }
136
137 fn visit_object(&mut self, shape_id: u32) -> Self::Output {
138 let shape = self
139 .evaluator
140 .interner()
141 .object_shape(ObjectShapeId(shape_id));
142
143 let result = self
144 .evaluator
145 .evaluate_object_index(&shape.properties, self.index_type);
146
147 if result == TypeId::UNDEFINED && self.is_generic_index() {
152 return None;
153 }
154
155 Some(result)
156 }
157
158 fn visit_object_with_index(&mut self, shape_id: u32) -> Self::Output {
159 let shape = self
160 .evaluator
161 .interner()
162 .object_shape(ObjectShapeId(shape_id));
163
164 let result = self
165 .evaluator
166 .evaluate_object_with_index(&shape, self.index_type);
167
168 if result == TypeId::UNDEFINED && self.is_generic_index() {
170 return None;
171 }
172
173 Some(result)
174 }
175
176 fn visit_union(&mut self, list_id: u32) -> Self::Output {
177 let members = self.evaluator.interner().type_list(TypeListId(list_id));
178 const MAX_UNION_INDEX_SIZE: usize = 100;
179 if members.len() > MAX_UNION_INDEX_SIZE {
180 self.evaluator.mark_depth_exceeded();
181 return Some(TypeId::ERROR);
182 }
183 let mut results = Vec::new();
184 for &member in members.iter() {
185 if self.evaluator.is_depth_exceeded() {
186 return Some(TypeId::ERROR);
187 }
188 let result = self.evaluator.recurse_index_access(member, self.index_type);
189 if result == TypeId::ERROR && self.evaluator.is_depth_exceeded() {
190 return Some(TypeId::ERROR);
191 }
192 if result != TypeId::UNDEFINED || self.evaluator.no_unchecked_indexed_access() {
193 results.push(result);
194 }
195 }
196 if results.is_empty() {
197 return Some(TypeId::UNDEFINED);
198 }
199 Some(self.evaluator.interner().union(results))
200 }
201
202 fn visit_intersection(&mut self, list_id: u32) -> Self::Output {
203 let members = self.evaluator.interner().type_list(TypeListId(list_id));
207 let mut results = Vec::new();
208 for &member in members.iter() {
209 let result = self.evaluator.recurse_index_access(member, self.index_type);
210 if result == TypeId::ERROR {
211 return Some(TypeId::ERROR);
212 }
213 if result != TypeId::UNDEFINED {
214 results.push(result);
215 }
216 }
217 if results.is_empty() {
218 Some(TypeId::UNDEFINED)
219 } else {
220 Some(self.evaluator.interner().union(results))
221 }
222 }
223
224 fn visit_lazy(&mut self, def_id: u32) -> Self::Output {
225 let def_id = crate::def::DefId(def_id);
228 if let Some(resolved) = self
229 .evaluator
230 .resolver()
231 .resolve_lazy(def_id, self.evaluator.interner())
232 {
233 return Some(
236 self.evaluator
237 .evaluate_index_access(resolved, self.index_type),
238 );
239 }
240 None
241 }
242
243 fn visit_array(&mut self, element_type: TypeId) -> Self::Output {
244 Some(
245 self.evaluator
246 .evaluate_array_index(element_type, self.index_type),
247 )
248 }
249
250 fn visit_tuple(&mut self, list_id: u32) -> Self::Output {
251 let elements = self.evaluator.interner().tuple_list(TupleListId(list_id));
252 Some(
253 self.evaluator
254 .evaluate_tuple_index(&elements, self.index_type),
255 )
256 }
257
258 fn visit_ref(&mut self, symbol_ref: u32) -> Self::Output {
259 let symbol_ref = SymbolRef(symbol_ref);
260 let resolved = if let Some(def_id) = self.evaluator.resolver().symbol_to_def_id(symbol_ref)
261 {
262 self.evaluator
263 .resolver()
264 .resolve_lazy(def_id, self.evaluator.interner())?
265 } else {
266 self.evaluator
267 .resolver()
268 .resolve_symbol_ref(symbol_ref, self.evaluator.interner())?
269 };
270 if resolved == self.object_type {
271 Some(
272 self.evaluator
273 .interner()
274 .index_access(self.object_type, self.index_type),
275 )
276 } else {
277 Some(
278 self.evaluator
279 .recurse_index_access(resolved, self.index_type),
280 )
281 }
282 }
283
284 fn visit_type_parameter(&mut self, param_info: &TypeParamInfo) -> Self::Output {
285 self.evaluate_type_param(param_info)
286 }
287
288 fn visit_infer(&mut self, param_info: &TypeParamInfo) -> Self::Output {
289 self.evaluate_type_param(param_info)
290 }
291
292 fn visit_readonly_type(&mut self, inner_type: TypeId) -> Self::Output {
293 Some(
294 self.evaluator
295 .recurse_index_access(inner_type, self.index_type),
296 )
297 }
298
299 fn visit_mapped(&mut self, mapped_id: u32) -> Self::Output {
300 let mapped = self
301 .evaluator
302 .interner()
303 .mapped_type(MappedTypeId(mapped_id));
304
305 if mapped.name_type.is_none() && mapped.constraint == self.index_type {
311 let mut subst = TypeSubstitution::new();
312 subst.insert(mapped.type_param.name, self.index_type);
313
314 let mut value_type = self.evaluator.evaluate(instantiate_type(
315 self.evaluator.interner(),
316 mapped.template,
317 &subst,
318 ));
319
320 if matches!(mapped.optional_modifier, Some(MappedModifier::Add)) {
322 value_type = self
323 .evaluator
324 .interner()
325 .union2(value_type, TypeId::UNDEFINED);
326 }
327
328 return Some(value_type);
329 }
330
331 None
332 }
333
334 fn visit_template_literal(&mut self, _template_id: u32) -> Self::Output {
335 self.evaluate_apparent_primitive(IntrinsicKind::String)
336 }
337
338 fn default_output() -> Self::Output {
339 None
340 }
341}
342
343struct ArrayKeyVisitor<'a> {
352 db: &'a dyn TypeDatabase,
353 element_type: TypeId,
354 array_member_types_cache: Option<Vec<TypeId>>,
355}
356
357impl<'a> ArrayKeyVisitor<'a> {
358 fn new(db: &'a dyn TypeDatabase, element_type: TypeId) -> Self {
359 Self {
360 db,
361 element_type,
362 array_member_types_cache: None,
363 }
364 }
365
366 fn evaluate(&mut self, index_type: TypeId) -> TypeId {
368 let result = self.visit_type(self.db, index_type);
369 result.unwrap_or(self.element_type)
370 }
371
372 fn get_array_member_types(&mut self) -> Vec<TypeId> {
373 self.array_member_types_cache
374 .get_or_insert_with(|| {
375 vec![
376 TypeId::NUMBER,
377 make_apparent_method_type(self.db, TypeId::ANY),
378 make_apparent_method_type(self.db, TypeId::BOOLEAN),
379 make_apparent_method_type(self.db, TypeId::NUMBER),
380 make_apparent_method_type(self.db, TypeId::VOID),
381 make_apparent_method_type(self.db, TypeId::STRING),
382 ]
383 })
384 .clone()
385 }
386}
387
388impl<'a> TypeVisitor for ArrayKeyVisitor<'a> {
389 type Output = Option<TypeId>;
390
391 fn visit_union(&mut self, list_id: u32) -> Self::Output {
392 let members = self.db.type_list(TypeListId(list_id));
393 let mut results = Vec::new();
394 for &member in members.iter() {
395 let result = self.evaluate(member);
396 if result != TypeId::UNDEFINED {
397 results.push(result);
398 }
399 }
400 if results.is_empty() {
401 Some(TypeId::UNDEFINED)
402 } else {
403 Some(self.db.union(results))
404 }
405 }
406
407 fn visit_intrinsic(&mut self, kind: IntrinsicKind) -> Self::Output {
408 match kind {
409 IntrinsicKind::Number => Some(self.element_type),
410 IntrinsicKind::String => Some(self.db.union(self.get_array_member_types())),
411 _ => Some(TypeId::UNDEFINED),
412 }
413 }
414
415 fn visit_literal(&mut self, value: &LiteralValue) -> Self::Output {
416 match value {
417 LiteralValue::Number(_) => Some(self.element_type),
418 LiteralValue::String(atom) => {
419 let name = self.db.resolve_atom_ref(*atom);
420 if utils::is_numeric_property_name(self.db, *atom) {
421 return Some(self.element_type);
422 }
423 if let Some(member) = get_array_member_kind(name.as_ref()) {
425 return match member {
426 ApparentMemberKind::Value(type_id) => Some(type_id),
427 ApparentMemberKind::Method(return_type) => {
428 Some(make_apparent_method_type(self.db, return_type))
429 }
430 };
431 }
432 Some(TypeId::UNDEFINED)
433 }
434 LiteralValue::Boolean(_) | LiteralValue::BigInt(_) => Some(TypeId::UNDEFINED),
436 }
437 }
438
439 fn default_output() -> Self::Output {
441 None
442 }
443}
444
445struct TupleKeyVisitor<'a> {
450 db: &'a dyn TypeDatabase,
451 elements: &'a [TupleElement],
452 array_member_types_cache: Option<Vec<TypeId>>,
453}
454
455impl<'a> TupleKeyVisitor<'a> {
456 fn new(db: &'a dyn TypeDatabase, elements: &'a [TupleElement]) -> Self {
457 Self {
458 db,
459 elements,
460 array_member_types_cache: None,
461 }
462 }
463
464 fn evaluate(&mut self, index_type: TypeId) -> TypeId {
466 let result = self.visit_type(self.db, index_type);
467 result.unwrap_or(TypeId::UNDEFINED)
468 }
469
470 fn tuple_element_type(&self, element: &TupleElement) -> TypeId {
472 let mut type_id = if element.rest {
473 self.rest_element_type(element.type_id)
474 } else {
475 element.type_id
476 };
477
478 if element.optional {
479 type_id = self.db.union2(type_id, TypeId::UNDEFINED);
480 }
481
482 type_id
483 }
484
485 fn rest_element_type(&self, type_id: TypeId) -> TypeId {
487 if let Some(elem) = array_element_type(self.db, type_id) {
488 return elem;
489 }
490 if let Some(elements) = tuple_list_id(self.db, type_id) {
491 let elements = self.db.tuple_list(elements);
492 let types: Vec<TypeId> = elements
493 .iter()
494 .map(|e| self.tuple_element_type(e))
495 .collect();
496 if types.is_empty() {
497 TypeId::NEVER
498 } else {
499 self.db.union(types)
500 }
501 } else {
502 type_id
503 }
504 }
505
506 fn tuple_index_literal(&self, idx: usize) -> Option<TypeId> {
508 for (logical_idx, element) in self.elements.iter().enumerate() {
509 if element.rest {
510 if let Some(rest_elements) = tuple_list_id(self.db, element.type_id) {
511 let rest_elements = self.db.tuple_list(rest_elements);
512 let inner_idx = idx.saturating_sub(logical_idx);
513 let inner_visitor = TupleKeyVisitor::new(self.db, &rest_elements);
515 return inner_visitor.tuple_index_literal(inner_idx);
516 }
517 return Some(self.tuple_element_type(element));
518 }
519
520 if logical_idx == idx {
521 return Some(self.tuple_element_type(element));
522 }
523 }
524
525 None
526 }
527
528 fn get_all_element_types(&self) -> Vec<TypeId> {
530 self.elements
531 .iter()
532 .map(|e| self.tuple_element_type(e))
533 .collect()
534 }
535
536 fn get_array_member_types(&mut self) -> Vec<TypeId> {
538 self.array_member_types_cache
539 .get_or_insert_with(|| {
540 vec![
541 TypeId::NUMBER,
542 make_apparent_method_type(self.db, TypeId::ANY),
543 make_apparent_method_type(self.db, TypeId::BOOLEAN),
544 make_apparent_method_type(self.db, TypeId::NUMBER),
545 make_apparent_method_type(self.db, TypeId::VOID),
546 make_apparent_method_type(self.db, TypeId::STRING),
547 ]
548 })
549 .clone()
550 }
551
552 fn get_array_member_kind(&self, name: &str) -> Option<ApparentMemberKind> {
554 if name == "length" {
555 return Some(ApparentMemberKind::Value(TypeId::NUMBER));
556 }
557 if is_member(name, ARRAY_METHODS_RETURN_ANY) {
558 return Some(ApparentMemberKind::Method(TypeId::ANY));
559 }
560 if is_member(name, ARRAY_METHODS_RETURN_BOOLEAN) {
561 return Some(ApparentMemberKind::Method(TypeId::BOOLEAN));
562 }
563 if is_member(name, ARRAY_METHODS_RETURN_NUMBER) {
564 return Some(ApparentMemberKind::Method(TypeId::NUMBER));
565 }
566 if is_member(name, ARRAY_METHODS_RETURN_VOID) {
567 return Some(ApparentMemberKind::Method(TypeId::VOID));
568 }
569 if is_member(name, ARRAY_METHODS_RETURN_STRING) {
570 return Some(ApparentMemberKind::Method(TypeId::STRING));
571 }
572 None
573 }
574}
575
576impl<'a> TypeVisitor for TupleKeyVisitor<'a> {
577 type Output = Option<TypeId>;
578
579 fn visit_union(&mut self, list_id: u32) -> Self::Output {
580 let members = self.db.type_list(TypeListId(list_id));
581 let mut results = Vec::new();
582 for &member in members.iter() {
583 let result = self.evaluate(member);
584 if result != TypeId::UNDEFINED {
585 results.push(result);
586 }
587 }
588 if results.is_empty() {
589 Some(TypeId::UNDEFINED)
590 } else {
591 Some(self.db.union(results))
592 }
593 }
594
595 fn visit_intrinsic(&mut self, kind: IntrinsicKind) -> Self::Output {
596 match kind {
597 IntrinsicKind::String => {
598 let mut types = self.get_all_element_types();
600 types.extend(self.get_array_member_types());
601 if types.is_empty() {
602 Some(TypeId::NEVER)
603 } else {
604 Some(self.db.union(types))
605 }
606 }
607 IntrinsicKind::Number => {
608 let all_types = self.get_all_element_types();
610 if all_types.is_empty() {
611 Some(TypeId::NEVER)
612 } else {
613 Some(self.db.union(all_types))
614 }
615 }
616 _ => Some(TypeId::UNDEFINED),
617 }
618 }
619
620 fn visit_literal(&mut self, value: &LiteralValue) -> Self::Output {
621 match value {
622 LiteralValue::Number(n) => {
623 let value = n.0;
624 if !value.is_finite() || value.fract() != 0.0 || value < 0.0 {
625 return Some(TypeId::UNDEFINED);
626 }
627 let idx = value as usize;
628 self.tuple_index_literal(idx).or(Some(TypeId::UNDEFINED))
629 }
630 LiteralValue::String(atom) => {
631 if utils::is_numeric_property_name(self.db, *atom) {
633 let name = self.db.resolve_atom_ref(*atom);
634 if let Ok(idx) = name.as_ref().parse::<i64>()
635 && let Ok(idx) = usize::try_from(idx)
636 {
637 return self.tuple_index_literal(idx).or(Some(TypeId::UNDEFINED));
638 }
639 return Some(TypeId::UNDEFINED);
640 }
641
642 let name = self.db.resolve_atom_ref(*atom);
644 if let Some(member) = self.get_array_member_kind(name.as_ref()) {
645 return match member {
646 ApparentMemberKind::Value(type_id) => Some(type_id),
647 ApparentMemberKind::Method(return_type) => {
648 Some(make_apparent_method_type(self.db, return_type))
649 }
650 };
651 }
652
653 Some(TypeId::UNDEFINED)
654 }
655 LiteralValue::Boolean(_) | LiteralValue::BigInt(_) => Some(TypeId::UNDEFINED),
657 }
658 }
659
660 fn default_output() -> Self::Output {
662 None
663 }
664}
665
666impl<'a, R: TypeResolver> TypeEvaluator<'a, R> {
667 pub(crate) fn recurse_index_access(
670 &mut self,
671 object_type: TypeId,
672 index_type: TypeId,
673 ) -> TypeId {
674 let index_access = self.interner().index_access(object_type, index_type);
675 self.evaluate(index_access)
676 }
677
678 pub fn evaluate_index_access(&mut self, object_type: TypeId, index_type: TypeId) -> TypeId {
682 let evaluated_object = self.evaluate(object_type);
683 let evaluated_index = self.evaluate(index_type);
684 if evaluated_object != object_type || evaluated_index != index_type {
685 return self.recurse_index_access(evaluated_object, evaluated_index);
687 }
688 if evaluated_object == TypeId::ANY || evaluated_index == TypeId::ANY {
691 return TypeId::ANY;
692 }
693
694 if let Some(members_id) = union_list_id(self.interner(), index_type) {
699 let members = self.interner().type_list(members_id);
700 const MAX_UNION_INDEX_SIZE: usize = 100;
702 if members.len() > MAX_UNION_INDEX_SIZE {
703 self.mark_depth_exceeded();
704 return TypeId::ERROR;
705 }
706 let mut results = Vec::new();
707 for &member in members.iter() {
708 if self.is_depth_exceeded() {
709 return TypeId::ERROR;
710 }
711 let result = self.recurse_index_access(object_type, member);
712 if result == TypeId::ERROR && self.is_depth_exceeded() {
713 return TypeId::ERROR;
714 }
715 if result != TypeId::UNDEFINED || self.no_unchecked_indexed_access() {
716 results.push(result);
717 }
718 }
719 if results.is_empty() {
720 return TypeId::UNDEFINED;
721 }
722 return self.interner().union(results);
723 }
724
725 let interner = self.interner();
726 let mut visitor = IndexAccessVisitor {
727 evaluator: self,
728 object_type,
729 index_type,
730 };
731 if let Some(result) = visitor.visit_type(interner, object_type) {
732 return result;
733 }
734
735 self.interner().index_access(object_type, index_type)
737 }
738
739 pub(crate) fn evaluate_object_index(
741 &self,
742 props: &[PropertyInfo],
743 index_type: TypeId,
744 ) -> TypeId {
745 if let Some(name) = literal_string(self.interner(), index_type) {
747 for prop in props {
748 if prop.name == name {
749 return self.optional_property_type(prop);
750 }
751 }
752 return TypeId::UNDEFINED;
754 }
755
756 if let Some(members) = union_list_id(self.interner(), index_type) {
758 let members = self.interner().type_list(members);
759 let mut results = Vec::new();
760 for &member in members.iter() {
761 let result = self.evaluate_object_index(props, member);
762 if result != TypeId::UNDEFINED || self.no_unchecked_indexed_access() {
763 results.push(result);
764 }
765 }
766 if results.is_empty() {
767 return TypeId::UNDEFINED;
768 }
769 return self.interner().union(results);
770 }
771
772 if index_type == TypeId::STRING {
774 let union = self.union_property_types(props);
775 return self.add_undefined_if_unchecked(union);
776 }
777
778 TypeId::UNDEFINED
779 }
780
781 pub(crate) fn evaluate_object_with_index(
783 &self,
784 shape: &ObjectShape,
785 index_type: TypeId,
786 ) -> TypeId {
787 if let Some(members) = union_list_id(self.interner(), index_type) {
789 let members = self.interner().type_list(members);
790 let mut results = Vec::new();
791 for &member in members.iter() {
792 let result = self.evaluate_object_with_index(shape, member);
793 if result != TypeId::UNDEFINED || self.no_unchecked_indexed_access() {
794 results.push(result);
795 }
796 }
797 if results.is_empty() {
798 return TypeId::UNDEFINED;
799 }
800 return self.interner().union(results);
801 }
802
803 if let Some(name) = literal_string(self.interner(), index_type) {
805 for prop in &shape.properties {
806 if prop.name == name {
807 return self.optional_property_type(prop);
808 }
809 }
810 if utils::is_numeric_property_name(self.interner(), name)
811 && let Some(number_index) = shape.number_index.as_ref()
812 {
813 return self.add_undefined_if_unchecked(number_index.value_type);
814 }
815 if let Some(string_index) = shape.string_index.as_ref() {
816 return self.add_undefined_if_unchecked(string_index.value_type);
817 }
818 return TypeId::UNDEFINED;
819 }
820
821 if literal_number(self.interner(), index_type).is_some() {
823 if let Some(number_index) = shape.number_index.as_ref() {
824 return self.add_undefined_if_unchecked(number_index.value_type);
825 }
826 if let Some(string_index) = shape.string_index.as_ref() {
827 return self.add_undefined_if_unchecked(string_index.value_type);
828 }
829 return TypeId::UNDEFINED;
830 }
831
832 if index_type == TypeId::STRING {
833 let result = if let Some(string_index) = shape.string_index.as_ref() {
834 string_index.value_type
835 } else {
836 self.union_property_types(&shape.properties)
837 };
838 return self.add_undefined_if_unchecked(result);
839 }
840
841 if index_type == TypeId::NUMBER {
842 let result = if let Some(number_index) = shape.number_index.as_ref() {
843 number_index.value_type
844 } else if let Some(string_index) = shape.string_index.as_ref() {
845 string_index.value_type
846 } else {
847 self.union_property_types(&shape.properties)
848 };
849 return self.add_undefined_if_unchecked(result);
850 }
851
852 TypeId::UNDEFINED
853 }
854
855 pub(crate) fn union_property_types(&self, props: &[PropertyInfo]) -> TypeId {
856 let all_types: Vec<TypeId> = props
857 .iter()
858 .map(|prop| self.optional_property_type(prop))
859 .collect();
860 if all_types.is_empty() {
861 TypeId::UNDEFINED
862 } else {
863 self.interner().union(all_types)
864 }
865 }
866
867 pub(crate) fn optional_property_type(&self, prop: &PropertyInfo) -> TypeId {
868 if prop.optional {
869 self.interner().union2(prop.type_id, TypeId::UNDEFINED)
870 } else {
871 prop.type_id
872 }
873 }
874
875 pub(crate) fn add_undefined_if_unchecked(&self, type_id: TypeId) -> TypeId {
876 if !self.no_unchecked_indexed_access() || type_id == TypeId::UNDEFINED {
877 return type_id;
878 }
879 self.interner().union2(type_id, TypeId::UNDEFINED)
880 }
881
882 pub(crate) fn rest_element_type(&self, type_id: TypeId) -> TypeId {
883 if let Some(elem) = array_element_type(self.interner(), type_id) {
884 return elem;
885 }
886 if let Some(elements) = tuple_list_id(self.interner(), type_id) {
887 let elements = self.interner().tuple_list(elements);
888 let types: Vec<TypeId> = elements
889 .iter()
890 .map(|e| self.tuple_element_type(e))
891 .collect();
892 if types.is_empty() {
893 TypeId::NEVER
894 } else {
895 self.interner().union(types)
896 }
897 } else {
898 type_id
899 }
900 }
901
902 pub(crate) fn tuple_element_type(&self, element: &TupleElement) -> TypeId {
903 let mut type_id = if element.rest {
904 self.rest_element_type(element.type_id)
905 } else {
906 element.type_id
907 };
908
909 if element.optional {
910 type_id = self.interner().union2(type_id, TypeId::UNDEFINED);
911 }
912
913 type_id
914 }
915
916 pub(crate) fn evaluate_tuple_index(
918 &self,
919 elements: &[TupleElement],
920 index_type: TypeId,
921 ) -> TypeId {
922 let mut visitor = TupleKeyVisitor::new(self.interner(), elements);
925 let result = visitor.evaluate(index_type);
926
927 self.add_undefined_if_unchecked(result)
929 }
930
931 pub(crate) fn evaluate_array_index(&self, elem: TypeId, index_type: TypeId) -> TypeId {
932 let mut visitor = ArrayKeyVisitor::new(self.interner(), elem);
935 let result = visitor.evaluate(index_type);
936
937 self.add_undefined_if_unchecked(result)
939 }
940}