1use crate::instantiation::instantiate::{TypeSubstitution, instantiate_type_with_infer};
7use crate::relations::subtype::{SubtypeChecker, TypeResolver};
8use crate::types::{
9 ConditionalType, ObjectShapeId, PropertyInfo, TupleElement, TypeData, TypeId, TypeParamInfo,
10};
11use rustc_hash::{FxHashMap, FxHashSet};
12use tracing::trace;
13
14use super::super::evaluate::TypeEvaluator;
15
16impl<'a, R: TypeResolver> TypeEvaluator<'a, R> {
17 const MAX_TAIL_RECURSION_DEPTH: usize = 1000;
21
22 pub fn evaluate_conditional(&mut self, initial_cond: &ConditionalType) -> TypeId {
37 let mut current_cond = initial_cond.clone();
39 let mut tail_recursion_count = 0;
40
41 loop {
42 let cond = ¤t_cond;
43
44 if let Some(result) = self.try_application_infer_match(cond) {
50 return result;
51 }
52
53 let check_type = self.evaluate(cond.check_type);
54 let extends_type = self.evaluate(cond.extends_type);
55
56 trace!(
57 check_raw = cond.check_type.0,
58 check_eval = check_type.0,
59 check_key = ?self.interner().lookup(check_type),
60 extends_raw = cond.extends_type.0,
61 extends_eval = extends_type.0,
62 extends_key = ?self.interner().lookup(extends_type),
63 "evaluate_conditional"
64 );
65
66 if cond.is_distributive && check_type == TypeId::NEVER {
67 return TypeId::NEVER;
68 }
69
70 if check_type == TypeId::ANY {
71 if self.type_contains_infer(extends_type) {
76 let mut bindings = FxHashMap::default();
77 let mut visited = FxHashSet::default();
78 let mut checker =
79 SubtypeChecker::with_resolver(self.interner(), self.resolver());
80 checker.allow_bivariant_rest = true;
81 self.match_infer_pattern(
82 check_type,
83 extends_type,
84 &mut bindings,
85 &mut visited,
86 &mut checker,
87 );
88 let true_sub = self.substitute_infer(cond.true_type, &bindings);
89 let false_sub = self.substitute_infer(cond.false_type, &bindings);
90 let true_eval = self.evaluate(true_sub);
91 let false_eval = self.evaluate(false_sub);
92 return self.interner().union2(true_eval, false_eval);
93 }
94 let true_eval = self.evaluate(cond.true_type);
95 let false_eval = self.evaluate(cond.false_type);
96 return self.interner().union2(true_eval, false_eval);
97 }
98
99 if cond.is_distributive
102 && let Some(TypeData::Union(members)) = self.interner().lookup(check_type)
103 {
104 let members = self.interner().type_list(members);
105 return self.distribute_conditional(
106 members.as_ref(),
107 check_type, extends_type,
109 cond.true_type,
110 cond.false_type,
111 );
112 }
113
114 if let Some(TypeData::Infer(info)) = self.interner().lookup(extends_type) {
115 if matches!(
116 self.interner().lookup(check_type),
117 Some(TypeData::TypeParameter(_) | TypeData::Infer(_))
118 ) {
119 return self.interner().conditional(cond.clone());
120 }
121
122 if check_type == TypeId::ANY {
123 let mut subst = TypeSubstitution::new();
124 subst.insert(info.name, check_type);
125 let true_eval = self.evaluate(instantiate_type_with_infer(
126 self.interner(),
127 cond.true_type,
128 &subst,
129 ));
130 let false_eval = self.evaluate(instantiate_type_with_infer(
131 self.interner(),
132 cond.false_type,
133 &subst,
134 ));
135 return self.interner().union2(true_eval, false_eval);
136 }
137
138 let mut subst = TypeSubstitution::new();
139 subst.insert(info.name, check_type);
140 let mut inferred = check_type;
141 if let Some(constraint) = info.constraint {
142 let mut checker =
143 SubtypeChecker::with_resolver(self.interner(), self.resolver());
144 checker.allow_bivariant_rest = true;
145 let Some(filtered) =
146 self.filter_inferred_by_constraint(inferred, constraint, &mut checker)
147 else {
148 let false_inst =
149 instantiate_type_with_infer(self.interner(), cond.false_type, &subst);
150 return self.evaluate(false_inst);
151 };
152 inferred = filtered;
153 }
154
155 subst.insert(info.name, inferred);
156
157 let true_inst =
158 instantiate_type_with_infer(self.interner(), cond.true_type, &subst);
159 return self.evaluate(true_inst);
160 }
161
162 let extends_unwrapped = match self.interner().lookup(extends_type) {
163 Some(TypeData::ReadonlyType(inner)) => inner,
164 _ => extends_type,
165 };
166 let check_unwrapped = match self.interner().lookup(check_type) {
167 Some(TypeData::ReadonlyType(inner)) => inner,
168 _ => check_type,
169 };
170
171 if let Some(TypeData::Array(ext_elem)) = self.interner().lookup(extends_unwrapped)
173 && let Some(TypeData::Infer(info)) = self.interner().lookup(ext_elem)
174 {
175 return self.eval_conditional_array_infer(cond, check_unwrapped, info);
176 }
177
178 if let Some(TypeData::Tuple(extends_elements)) =
180 self.interner().lookup(extends_unwrapped)
181 {
182 let extends_elements = self.interner().tuple_list(extends_elements);
183 if extends_elements.len() == 1
184 && !extends_elements[0].rest
185 && let Some(TypeData::Infer(info)) =
186 self.interner().lookup(extends_elements[0].type_id)
187 {
188 return self.eval_conditional_tuple_infer(
189 cond,
190 check_unwrapped,
191 &extends_elements[0],
192 info,
193 );
194 }
195 }
196
197 if let Some(extends_shape_id) = match self.interner().lookup(extends_unwrapped) {
199 Some(TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id)) => {
200 Some(shape_id)
201 }
202 _ => None,
203 } && let Some(result) =
204 self.eval_conditional_object_infer(cond, check_unwrapped, extends_shape_id)
205 {
206 return result;
207 }
208
209 if let Some(TypeData::TypeParameter(param)) = self.interner().lookup(check_type) {
211 if extends_type == TypeId::NEVER {
215 return self.evaluate(cond.false_type);
216 }
217
218 if check_type == extends_type {
222 return self.evaluate(cond.true_type);
223 }
224
225 if self.type_contains_infer(extends_type)
230 && let Some(constraint) = param.constraint
231 {
232 let mut checker =
233 SubtypeChecker::with_resolver(self.interner(), self.resolver());
234 checker.allow_bivariant_rest = true;
235 let mut bindings = FxHashMap::default();
236 let mut visited = FxHashSet::default();
237 if self.match_infer_pattern(
238 constraint,
239 extends_type,
240 &mut bindings,
241 &mut visited,
242 &mut checker,
243 ) {
244 let substituted_true = self.substitute_infer(cond.true_type, &bindings);
245 return self.evaluate(substituted_true);
246 }
247 }
248 if let Some(constraint) = param.constraint {
255 let evaluated_constraint = self.evaluate(constraint);
256 if !self.type_contains_infer(extends_type) {
257 let mut checker =
258 SubtypeChecker::with_resolver(self.interner(), self.resolver());
259 if !checker.is_subtype_of(evaluated_constraint, extends_type) {
260 if tail_recursion_count < Self::MAX_TAIL_RECURSION_DEPTH
263 && let Some(TypeData::Conditional(next_cond_id)) =
264 self.interner().lookup(cond.false_type)
265 {
266 let next_cond = self.interner().conditional_type(next_cond_id);
267 current_cond = (*next_cond).clone();
268 tail_recursion_count += 1;
269 continue;
270 }
271 return self.evaluate(cond.false_type);
272 }
273 let constraint_is_union = matches!(
284 self.interner().lookup(evaluated_constraint),
285 Some(TypeData::Union(_))
286 );
287 if !constraint_is_union || !cond.is_distributive {
288 if tail_recursion_count < Self::MAX_TAIL_RECURSION_DEPTH
290 && let Some(TypeData::Conditional(next_cond_id)) =
291 self.interner().lookup(cond.true_type)
292 {
293 let next_cond = self.interner().conditional_type(next_cond_id);
294 current_cond = (*next_cond).clone();
295 tail_recursion_count += 1;
296 continue;
297 }
298 return self.evaluate(cond.true_type);
299 }
300 }
301 }
302
303 return self.interner().conditional(cond.clone());
305 }
306
307 let mut checker = SubtypeChecker::with_resolver(self.interner(), self.resolver());
309 checker.allow_bivariant_rest = true;
310
311 if self.type_contains_infer(extends_type) {
312 let mut bindings = FxHashMap::default();
313 let mut visited = FxHashSet::default();
314 if self.match_infer_pattern(
315 check_type,
316 extends_type,
317 &mut bindings,
318 &mut visited,
319 &mut checker,
320 ) {
321 let substituted_true = self.substitute_infer(cond.true_type, &bindings);
322 return self.evaluate(substituted_true);
323 }
324
325 if tail_recursion_count < Self::MAX_TAIL_RECURSION_DEPTH {
328 if let Some(TypeData::Conditional(next_cond_id)) =
329 self.interner().lookup(cond.false_type)
330 {
331 let next_cond = self.interner().conditional_type(next_cond_id);
332 current_cond = (*next_cond).clone();
333 tail_recursion_count += 1;
334 continue;
335 }
336 if let Some(TypeData::Application(_)) = self.interner().lookup(cond.false_type)
340 {
341 let expanded = self.evaluate(cond.false_type);
342 if let Some(TypeData::Conditional(next_cond_id)) =
343 self.interner().lookup(expanded)
344 {
345 let next_cond = self.interner().conditional_type(next_cond_id);
346 current_cond = (*next_cond).clone();
347 tail_recursion_count += 1;
348 continue;
349 }
350 return expanded;
351 }
352 }
353
354 return self.evaluate(cond.false_type);
356 }
357
358 let mut strict_checker =
361 SubtypeChecker::with_resolver(self.interner(), self.resolver());
362 let is_sub = strict_checker.is_subtype_of(check_type, extends_type);
363 trace!(
364 check = check_type.0,
365 extends = extends_type.0,
366 is_subtype = is_sub,
367 "conditional subtype check result"
368 );
369 let result_branch = if is_sub {
370 cond.true_type
372 } else {
373 cond.false_type
377 };
378
379 if tail_recursion_count < Self::MAX_TAIL_RECURSION_DEPTH {
382 if let Some(TypeData::Conditional(next_cond_id)) =
383 self.interner().lookup(result_branch)
384 {
385 let next_cond = self.interner().conditional_type(next_cond_id);
386 current_cond = (*next_cond).clone();
387 tail_recursion_count += 1;
388 continue;
389 }
390 if let Some(TypeData::Application(_)) = self.interner().lookup(result_branch) {
393 let expanded = self.evaluate(result_branch);
394 if let Some(TypeData::Conditional(next_cond_id)) =
395 self.interner().lookup(expanded)
396 {
397 let next_cond = self.interner().conditional_type(next_cond_id);
398 current_cond = (*next_cond).clone();
399 tail_recursion_count += 1;
400 continue;
401 }
402 return expanded;
403 }
404 }
405
406 return self.evaluate(result_branch);
408 }
409 }
410
411 pub(crate) fn distribute_conditional(
414 &mut self,
415 members: &[TypeId],
416 original_check_type: TypeId,
417 extends_type: TypeId,
418 true_type: TypeId,
419 false_type: TypeId,
420 ) -> TypeId {
421 const MAX_DISTRIBUTION_SIZE: usize = 100;
423 if members.len() > MAX_DISTRIBUTION_SIZE {
424 self.mark_depth_exceeded();
425 return TypeId::ERROR;
426 }
427
428 let mut results: Vec<TypeId> = Vec::with_capacity(members.len());
429
430 for &member in members {
431 if self.is_depth_exceeded() {
433 return TypeId::ERROR;
434 }
435
436 let substituted_true_type = if true_type == original_check_type {
440 member
441 } else {
442 true_type
443 };
444 let substituted_false_type = if false_type == original_check_type {
445 member
446 } else {
447 false_type
448 };
449
450 let member_cond = ConditionalType {
452 check_type: member,
453 extends_type,
454 true_type: substituted_true_type,
455 false_type: substituted_false_type,
456 is_distributive: false,
457 };
458
459 let cond_type = self.interner().conditional(member_cond);
461 let result = self.evaluate(cond_type);
462 if result == TypeId::ERROR && self.is_depth_exceeded() {
464 return TypeId::ERROR;
465 }
466 results.push(result);
467 }
468
469 self.interner().union(results)
471 }
472
473 fn eval_conditional_array_infer(
475 &mut self,
476 cond: &ConditionalType,
477 check_unwrapped: TypeId,
478 info: TypeParamInfo,
479 ) -> TypeId {
480 if matches!(
481 self.interner().lookup(check_unwrapped),
482 Some(TypeData::TypeParameter(_) | TypeData::Infer(_))
483 ) {
484 return self.interner().conditional(cond.clone());
485 }
486
487 let inferred = match self.interner().lookup(check_unwrapped) {
488 Some(TypeData::Array(elem)) => Some(elem),
489 Some(TypeData::Tuple(elements)) => {
490 let elements = self.interner().tuple_list(elements);
491 let mut parts = Vec::new();
492 for element in elements.iter() {
493 if element.rest {
494 let rest_type = self.rest_element_type(element.type_id);
495 parts.push(rest_type);
496 } else {
497 let elem_type = if element.optional {
498 self.interner().union2(element.type_id, TypeId::UNDEFINED)
499 } else {
500 element.type_id
501 };
502 parts.push(elem_type);
503 }
504 }
505 if parts.is_empty() {
506 None
507 } else {
508 Some(self.interner().union(parts))
509 }
510 }
511 Some(TypeData::Union(members)) => {
512 let members = self.interner().type_list(members);
513 let mut parts = Vec::new();
514 for &member in members.iter() {
515 match self.interner().lookup(member) {
516 Some(TypeData::Array(elem)) => parts.push(elem),
517 Some(TypeData::ReadonlyType(inner)) => {
518 let Some(TypeData::Array(elem)) = self.interner().lookup(inner) else {
519 return self.evaluate(cond.false_type);
520 };
521 parts.push(elem);
522 }
523 _ => return self.evaluate(cond.false_type),
524 }
525 }
526 if parts.is_empty() {
527 None
528 } else if parts.len() == 1 {
529 Some(parts[0])
530 } else {
531 Some(self.interner().union(parts))
532 }
533 }
534 _ => None,
535 };
536
537 let Some(mut inferred) = inferred else {
538 return self.evaluate(cond.false_type);
539 };
540
541 let mut subst = TypeSubstitution::new();
542 subst.insert(info.name, inferred);
543
544 if let Some(constraint) = info.constraint {
545 let mut checker = SubtypeChecker::with_resolver(self.interner(), self.resolver());
546 checker.allow_bivariant_rest = true;
547 let is_union = matches!(self.interner().lookup(inferred), Some(TypeData::Union(_)));
548 if is_union && !cond.is_distributive {
549 inferred = self.filter_inferred_by_constraint_or_undefined(
551 inferred,
552 constraint,
553 &mut checker,
554 );
555 } else {
556 if !checker.is_subtype_of(inferred, constraint) {
558 return self.evaluate(cond.false_type);
559 }
560 }
561 subst.insert(info.name, inferred);
562 }
563
564 let true_inst = instantiate_type_with_infer(self.interner(), cond.true_type, &subst);
565 self.evaluate(true_inst)
566 }
567
568 fn eval_conditional_tuple_infer(
570 &mut self,
571 cond: &ConditionalType,
572 check_unwrapped: TypeId,
573 extends_elem: &TupleElement,
574 info: TypeParamInfo,
575 ) -> TypeId {
576 if matches!(
577 self.interner().lookup(check_unwrapped),
578 Some(TypeData::TypeParameter(_) | TypeData::Infer(_))
579 ) {
580 return self.interner().conditional(cond.clone());
581 }
582
583 let inferred = match self.interner().lookup(check_unwrapped) {
584 Some(TypeData::Tuple(check_elements)) => {
585 let check_elements = self.interner().tuple_list(check_elements);
586 if check_elements.is_empty() {
587 extends_elem.optional.then_some(TypeId::UNDEFINED)
588 } else if check_elements.len() == 1 && !check_elements[0].rest {
589 let elem = &check_elements[0];
590 Some(if elem.optional {
591 self.interner().union2(elem.type_id, TypeId::UNDEFINED)
592 } else {
593 elem.type_id
594 })
595 } else {
596 None
597 }
598 }
599 Some(TypeData::Union(members)) => {
600 let members = self.interner().type_list(members);
601 let mut inferred_members = Vec::new();
602 for &member in members.iter() {
603 let member_type = match self.interner().lookup(member) {
604 Some(TypeData::ReadonlyType(inner)) => inner,
605 _ => member,
606 };
607 match self.interner().lookup(member_type) {
608 Some(TypeData::Tuple(check_elements)) => {
609 let check_elements = self.interner().tuple_list(check_elements);
610 if check_elements.is_empty() {
611 if extends_elem.optional {
612 inferred_members.push(TypeId::UNDEFINED);
613 continue;
614 }
615 return self.evaluate(cond.false_type);
616 }
617 if check_elements.len() == 1 && !check_elements[0].rest {
618 let elem = &check_elements[0];
619 let elem_type = if elem.optional {
620 self.interner().union2(elem.type_id, TypeId::UNDEFINED)
621 } else {
622 elem.type_id
623 };
624 inferred_members.push(elem_type);
625 } else {
626 return self.evaluate(cond.false_type);
627 }
628 }
629 _ => return self.evaluate(cond.false_type),
630 }
631 }
632 if inferred_members.is_empty() {
633 None
634 } else if inferred_members.len() == 1 {
635 Some(inferred_members[0])
636 } else {
637 Some(self.interner().union(inferred_members))
638 }
639 }
640 _ => None,
641 };
642
643 let Some(mut inferred) = inferred else {
644 return self.evaluate(cond.false_type);
645 };
646
647 let mut subst = TypeSubstitution::new();
648 subst.insert(info.name, inferred);
649
650 if let Some(constraint) = info.constraint {
651 let mut checker = SubtypeChecker::with_resolver(self.interner(), self.resolver());
652 checker.allow_bivariant_rest = true;
653 let Some(filtered) =
654 self.filter_inferred_by_constraint(inferred, constraint, &mut checker)
655 else {
656 let false_inst =
657 instantiate_type_with_infer(self.interner(), cond.false_type, &subst);
658 return self.evaluate(false_inst);
659 };
660 inferred = filtered;
661 subst.insert(info.name, inferred);
662 }
663
664 let true_inst = instantiate_type_with_infer(self.interner(), cond.true_type, &subst);
665 self.evaluate(true_inst)
666 }
667
668 fn eval_conditional_object_infer(
670 &mut self,
671 cond: &ConditionalType,
672 check_unwrapped: TypeId,
673 extends_shape_id: ObjectShapeId,
674 ) -> Option<TypeId> {
675 let extends_shape = self.interner().object_shape(extends_shape_id);
676 let mut infer_prop = None;
677 let mut infer_nested = None;
678
679 for prop in &extends_shape.properties {
680 if let Some(TypeData::Infer(info)) = self.interner().lookup(prop.type_id) {
681 if infer_prop.is_some() || infer_nested.is_some() {
682 return None;
683 }
684 infer_prop = Some((prop.name, info, prop.optional));
685 continue;
686 }
687
688 let nested_type = match self.interner().lookup(prop.type_id) {
689 Some(TypeData::ReadonlyType(inner)) => inner,
690 _ => prop.type_id,
691 };
692 if let Some(nested_shape_id) = match self.interner().lookup(nested_type) {
693 Some(TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id)) => {
694 Some(shape_id)
695 }
696 _ => None,
697 } {
698 let nested_shape = self.interner().object_shape(nested_shape_id);
699 let mut nested_infer = None;
700 for nested_prop in &nested_shape.properties {
701 if let Some(TypeData::Infer(info)) = self.interner().lookup(nested_prop.type_id)
702 {
703 if nested_infer.is_some() {
704 nested_infer = None;
705 break;
706 }
707 nested_infer = Some((nested_prop.name, info));
708 }
709 }
710 if let Some((nested_name, info)) = nested_infer {
711 if infer_prop.is_some() || infer_nested.is_some() {
712 return None;
713 }
714 infer_nested = Some((prop.name, nested_name, info));
715 }
716 }
717 }
718
719 if let Some((prop_name, info, prop_optional)) = infer_prop {
720 return Some(self.eval_conditional_object_prop_infer(
721 cond,
722 check_unwrapped,
723 prop_name,
724 info,
725 prop_optional,
726 ));
727 }
728
729 if let Some((outer_name, inner_name, info)) = infer_nested {
730 return Some(self.eval_conditional_object_nested_infer(
731 cond,
732 check_unwrapped,
733 outer_name,
734 inner_name,
735 info,
736 ));
737 }
738
739 None
740 }
741
742 fn eval_conditional_object_prop_infer(
744 &mut self,
745 cond: &ConditionalType,
746 check_unwrapped: TypeId,
747 prop_name: tsz_common::interner::Atom,
748 info: TypeParamInfo,
749 prop_optional: bool,
750 ) -> TypeId {
751 if matches!(
752 self.interner().lookup(check_unwrapped),
753 Some(TypeData::TypeParameter(_) | TypeData::Infer(_))
754 ) {
755 return self.interner().conditional(cond.clone());
756 }
757
758 let inferred = match self.interner().lookup(check_unwrapped) {
759 Some(TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id)) => {
760 let shape = self.interner().object_shape(shape_id);
761 shape
762 .properties
763 .iter()
764 .find(|prop| prop.name == prop_name)
765 .map(|prop| {
766 if prop_optional {
767 self.optional_property_type(prop)
768 } else {
769 prop.type_id
770 }
771 })
772 .or_else(|| prop_optional.then_some(TypeId::UNDEFINED))
773 }
774 Some(TypeData::Union(members)) => {
775 let members = self.interner().type_list(members);
776 let mut inferred_members = Vec::new();
777 for &member in members.iter() {
778 let member_unwrapped = match self.interner().lookup(member) {
779 Some(TypeData::ReadonlyType(inner)) => inner,
780 _ => member,
781 };
782 match self.interner().lookup(member_unwrapped) {
783 Some(TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id)) => {
784 let shape = self.interner().object_shape(shape_id);
785 if let Some(prop) =
786 PropertyInfo::find_in_slice(&shape.properties, prop_name)
787 {
788 inferred_members.push(if prop_optional {
789 self.optional_property_type(prop)
790 } else {
791 prop.type_id
792 });
793 } else if prop_optional {
794 inferred_members.push(TypeId::UNDEFINED);
795 } else {
796 return self.evaluate(cond.false_type);
797 }
798 }
799 _ => return self.evaluate(cond.false_type),
800 }
801 }
802 if inferred_members.is_empty() {
803 None
804 } else if inferred_members.len() == 1 {
805 Some(inferred_members[0])
806 } else {
807 Some(self.interner().union(inferred_members))
808 }
809 }
810 _ => None,
811 };
812
813 let Some(mut inferred) = inferred else {
814 return self.evaluate(cond.false_type);
815 };
816
817 let mut subst = TypeSubstitution::new();
818 subst.insert(info.name, inferred);
819
820 if let Some(constraint) = info.constraint {
821 let mut checker = SubtypeChecker::with_resolver(self.interner(), self.resolver());
822 checker.allow_bivariant_rest = true;
823 let is_union = matches!(self.interner().lookup(inferred), Some(TypeData::Union(_)));
824 if prop_optional {
825 let Some(filtered) =
826 self.filter_inferred_by_constraint(inferred, constraint, &mut checker)
827 else {
828 let false_inst =
829 instantiate_type_with_infer(self.interner(), cond.false_type, &subst);
830 return self.evaluate(false_inst);
831 };
832 inferred = filtered;
833 } else if is_union || cond.is_distributive {
834 inferred = self.filter_inferred_by_constraint_or_undefined(
836 inferred,
837 constraint,
838 &mut checker,
839 );
840 } else {
841 if !checker.is_subtype_of(inferred, constraint) {
843 return self.evaluate(cond.false_type);
844 }
845 }
846 subst.insert(info.name, inferred);
847 }
848
849 let true_inst = instantiate_type_with_infer(self.interner(), cond.true_type, &subst);
850 self.evaluate(true_inst)
851 }
852
853 fn eval_conditional_object_nested_infer(
855 &mut self,
856 cond: &ConditionalType,
857 check_unwrapped: TypeId,
858 outer_name: tsz_common::interner::Atom,
859 inner_name: tsz_common::interner::Atom,
860 info: TypeParamInfo,
861 ) -> TypeId {
862 if matches!(
863 self.interner().lookup(check_unwrapped),
864 Some(TypeData::TypeParameter(_) | TypeData::Infer(_))
865 ) {
866 return self.interner().conditional(cond.clone());
867 }
868
869 let inferred = match self.interner().lookup(check_unwrapped) {
870 Some(TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id)) => {
871 let shape = self.interner().object_shape(shape_id);
872 shape
873 .properties
874 .iter()
875 .find(|prop| prop.name == outer_name)
876 .and_then(|prop| {
877 let inner_type = match self.interner().lookup(prop.type_id) {
878 Some(TypeData::ReadonlyType(inner)) => inner,
879 _ => prop.type_id,
880 };
881 match self.interner().lookup(inner_type) {
882 Some(
883 TypeData::Object(inner_shape_id)
884 | TypeData::ObjectWithIndex(inner_shape_id),
885 ) => {
886 let inner_shape = self.interner().object_shape(inner_shape_id);
887 inner_shape
888 .properties
889 .iter()
890 .find(|prop| prop.name == inner_name)
891 .map(|prop| prop.type_id)
892 }
893 _ => None,
894 }
895 })
896 }
897 Some(TypeData::Union(members)) => {
898 let members = self.interner().type_list(members);
899 let mut inferred_members = Vec::new();
900 for &member in members.iter() {
901 let member_unwrapped = match self.interner().lookup(member) {
902 Some(TypeData::ReadonlyType(inner)) => inner,
903 _ => member,
904 };
905 let Some(TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id)) =
906 self.interner().lookup(member_unwrapped)
907 else {
908 return self.evaluate(cond.false_type);
909 };
910 let shape = self.interner().object_shape(shape_id);
911 let Some(prop) = PropertyInfo::find_in_slice(&shape.properties, outer_name)
912 else {
913 return self.evaluate(cond.false_type);
914 };
915 let inner_type = match self.interner().lookup(prop.type_id) {
916 Some(TypeData::ReadonlyType(inner)) => inner,
917 _ => prop.type_id,
918 };
919 let Some(
920 TypeData::Object(inner_shape_id)
921 | TypeData::ObjectWithIndex(inner_shape_id),
922 ) = self.interner().lookup(inner_type)
923 else {
924 return self.evaluate(cond.false_type);
925 };
926 let inner_shape = self.interner().object_shape(inner_shape_id);
927 let Some(inner_prop) = inner_shape
928 .properties
929 .iter()
930 .find(|prop| prop.name == inner_name)
931 else {
932 return self.evaluate(cond.false_type);
933 };
934 inferred_members.push(inner_prop.type_id);
935 }
936 if inferred_members.is_empty() {
937 None
938 } else if inferred_members.len() == 1 {
939 Some(inferred_members[0])
940 } else {
941 Some(self.interner().union(inferred_members))
942 }
943 }
944 _ => None,
945 };
946
947 let Some(mut inferred) = inferred else {
948 return self.evaluate(cond.false_type);
949 };
950
951 let mut subst = TypeSubstitution::new();
952 subst.insert(info.name, inferred);
953
954 if let Some(constraint) = info.constraint {
955 let mut checker = SubtypeChecker::with_resolver(self.interner(), self.resolver());
956 checker.allow_bivariant_rest = true;
957 let is_union = matches!(self.interner().lookup(inferred), Some(TypeData::Union(_)));
958 if is_union || cond.is_distributive {
959 inferred = self.filter_inferred_by_constraint_or_undefined(
961 inferred,
962 constraint,
963 &mut checker,
964 );
965 } else {
966 if !checker.is_subtype_of(inferred, constraint) {
968 return self.evaluate(cond.false_type);
969 }
970 }
971 subst.insert(info.name, inferred);
972 }
973
974 let true_inst = instantiate_type_with_infer(self.interner(), cond.true_type, &subst);
975 self.evaluate(true_inst)
976 }
977
978 fn try_application_infer_match(&mut self, cond: &ConditionalType) -> Option<TypeId> {
986 let Some(TypeData::Application(_)) = self.interner().lookup(cond.extends_type) else {
992 return None;
993 };
994
995 if !self.type_contains_infer(cond.extends_type) {
996 return None;
997 }
998
999 let check_type = cond.check_type;
1002
1003 if check_type == TypeId::ANY || check_type == TypeId::NEVER {
1005 return None;
1006 }
1007 if matches!(
1008 self.interner().lookup(check_type),
1009 Some(TypeData::TypeParameter(_))
1010 ) {
1011 return None;
1012 }
1013
1014 let mut checker = SubtypeChecker::with_resolver(self.interner(), self.resolver());
1018 checker.allow_bivariant_rest = true;
1019 let mut bindings = FxHashMap::default();
1020 let mut visited = FxHashSet::default();
1021 if self.match_infer_pattern(
1022 check_type,
1023 cond.extends_type,
1024 &mut bindings,
1025 &mut visited,
1026 &mut checker,
1027 ) && !bindings.is_empty()
1028 {
1029 let substituted_true = self.substitute_infer(cond.true_type, &bindings);
1030 return Some(self.evaluate(substituted_true));
1031 }
1032
1033 None
1034 }
1035}