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