1use crate::types::TypeListId;
18use crate::visitor::TypeVisitor;
19use crate::{IntrinsicKind, LiteralValue, QueryDatabase, TypeData, TypeDatabase, TypeId};
20
21#[derive(Clone, Debug, PartialEq)]
23pub enum BinaryOpResult {
24 Success(TypeId),
26
27 TypeError {
29 left: TypeId,
30 right: TypeId,
31 op: &'static str,
32 },
33}
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
37pub enum PrimitiveClass {
38 String,
39 Number,
40 Boolean,
41 Bigint,
42 Symbol,
43 Null,
44 Undefined,
45}
46
47macro_rules! primitive_visitor {
68 ($name:ident, $ik:expr, $lit_pat:pat => $lit_result:expr $(, $feat:ident)*) => {
69 struct $name<'a> { _db: &'a dyn TypeDatabase }
70 impl<'a> TypeVisitor for $name<'a> {
71 type Output = bool;
72 fn visit_intrinsic(&mut self, kind: IntrinsicKind) -> bool { kind == $ik }
73 fn visit_literal(&mut self, value: &LiteralValue) -> bool {
74 match value { $lit_pat => $lit_result, _ => false }
75 }
76 $(primitive_visitor!(@method $feat);)*
77 fn default_output() -> bool { false }
78 }
79 };
80 (@method check_union_all) => {
81 fn visit_union(&mut self, list_id: u32) -> bool {
82 let members = self._db.type_list(TypeListId(list_id));
83 !members.is_empty() && members.iter().all(|&m| self.visit_type(self._db, m))
84 }
85 };
86 (@method check_constraint) => {
87 fn visit_type_parameter(&mut self, info: &crate::types::TypeParamInfo) -> bool {
88 info.constraint.map(|c| self.visit_type(self._db, c)).unwrap_or(false)
89 }
90 fn visit_infer(&mut self, info: &crate::types::TypeParamInfo) -> bool {
91 info.constraint.map(|c| self.visit_type(self._db, c)).unwrap_or(false)
92 }
93 };
94 (@method recurse_enum) => {
95 fn visit_enum(&mut self, _def_id: u32, member_type: TypeId) -> bool {
96 self.visit_type(self._db, member_type)
97 }
98 };
99 (@method ref_conservative) => {
100 fn visit_ref(&mut self, _symbol_ref: u32) -> bool { true }
101 };
102 (@method match_template_literal) => {
103 fn visit_template_literal(&mut self, _template_id: u32) -> bool { true }
104 };
105 (@method match_unique_symbol) => {
106 fn visit_unique_symbol(&mut self, _symbol_ref: u32) -> bool { true }
107 };
108 (@method check_intersection_any) => {
109 fn visit_intersection(&mut self, list_id: u32) -> bool {
110 let members = self._db.type_list(TypeListId(list_id));
111 members.iter().any(|&m| self.visit_type(self._db, m))
112 }
113 };
114}
115
116primitive_visitor!(NumberLikeVisitor, IntrinsicKind::Number,
117 LiteralValue::Number(_) => true,
118 check_union_all, check_constraint, recurse_enum, check_intersection_any);
119
120primitive_visitor!(StringLikeVisitor, IntrinsicKind::String,
121 LiteralValue::String(_) => true,
122 check_union_all, check_constraint, recurse_enum, match_template_literal, check_intersection_any);
123
124primitive_visitor!(BigIntLikeVisitor, IntrinsicKind::Bigint,
125 LiteralValue::BigInt(_) => true,
126 check_union_all, check_constraint, recurse_enum, check_intersection_any);
127
128primitive_visitor!(BooleanLikeVisitor, IntrinsicKind::Boolean,
129 LiteralValue::Boolean(_) => true);
130
131struct InstanceofLeftOperandVisitor<'a> {
132 _db: &'a dyn TypeDatabase,
133}
134
135impl<'a> TypeVisitor for InstanceofLeftOperandVisitor<'a> {
136 type Output = bool;
137
138 fn visit_intrinsic(&mut self, kind: IntrinsicKind) -> bool {
139 matches!(
140 kind,
141 IntrinsicKind::Any | IntrinsicKind::Unknown | IntrinsicKind::Object
142 )
143 }
144
145 fn visit_literal(&mut self, _value: &LiteralValue) -> bool {
146 false
147 }
148
149 fn visit_type_parameter(&mut self, _info: &crate::types::TypeParamInfo) -> bool {
150 true
151 }
152
153 fn visit_object(&mut self, _shape_id: u32) -> bool {
154 true
155 }
156 fn visit_object_with_index(&mut self, _shape_id: u32) -> bool {
157 true
158 }
159 fn visit_array(&mut self, _element_type: TypeId) -> bool {
160 true
161 }
162 fn visit_tuple(&mut self, _list_id: u32) -> bool {
163 true
164 }
165 fn visit_function(&mut self, _shape_id: u32) -> bool {
166 true
167 }
168 fn visit_callable(&mut self, _shape_id: u32) -> bool {
169 true
170 }
171 fn visit_application(&mut self, _app_id: u32) -> bool {
172 true
173 }
174 fn visit_readonly_type(&mut self, _type_id: TypeId) -> bool {
175 true
176 }
177
178 fn visit_union(&mut self, list_id: u32) -> bool {
179 let members = self._db.type_list(crate::types::TypeListId(list_id));
180 let mut any_valid = false;
181 for &m in members.iter() {
182 if self.visit_type(self._db, m) {
183 any_valid = true;
184 break;
185 }
186 }
187 any_valid
188 }
189
190 fn visit_intersection(&mut self, list_id: u32) -> bool {
191 let members = self._db.type_list(crate::types::TypeListId(list_id));
192 let mut any_valid = false;
193 for &m in members.iter() {
194 if self.visit_type(self._db, m) {
195 any_valid = true;
196 break;
197 }
198 }
199 any_valid
200 }
201
202 fn default_output() -> bool {
203 false
204 }
205}
206
207struct SymbolLikeVisitor<'a> {
208 _db: &'a dyn TypeDatabase,
209}
210
211impl<'a> TypeVisitor for SymbolLikeVisitor<'a> {
212 type Output = bool;
213
214 fn visit_intrinsic(&mut self, kind: IntrinsicKind) -> bool {
215 kind == IntrinsicKind::Symbol
216 }
217
218 fn visit_literal(&mut self, value: &LiteralValue) -> bool {
219 let _ = value;
220 false
221 }
222
223 fn visit_ref(&mut self, _symbol_ref: u32) -> bool {
224 true
225 }
226
227 fn visit_unique_symbol(&mut self, _symbol_ref: u32) -> bool {
228 true
229 }
230
231 fn default_output() -> bool {
232 false
233 }
234}
235
236struct OrderableVisitor<'a> {
239 _db: &'a dyn TypeDatabase,
240}
241
242impl<'a> TypeVisitor for OrderableVisitor<'a> {
243 type Output = bool;
244
245 fn visit_intrinsic(&mut self, kind: IntrinsicKind) -> bool {
246 matches!(
247 kind,
248 IntrinsicKind::String | IntrinsicKind::Number | IntrinsicKind::Bigint
249 )
250 }
251
252 fn visit_literal(&mut self, value: &LiteralValue) -> bool {
253 matches!(
254 value,
255 LiteralValue::String(_) | LiteralValue::Number(_) | LiteralValue::BigInt(_)
256 )
257 }
258
259 fn visit_union(&mut self, list_id: u32) -> bool {
260 let members = self._db.type_list(TypeListId(list_id));
261 !members.is_empty() && members.iter().all(|&m| self.visit_type(self._db, m))
262 }
263
264 fn visit_enum(&mut self, _def_id: u32, member_type: TypeId) -> bool {
265 self.visit_type(self._db, member_type)
266 }
267
268 fn visit_template_literal(&mut self, _template_id: u32) -> bool {
269 true }
271
272 fn visit_intersection(&mut self, list_id: u32) -> bool {
273 let members = self._db.type_list(TypeListId(list_id));
274 members.iter().any(|&m| self.visit_type(self._db, m))
275 }
276
277 fn visit_type_parameter(&mut self, info: &crate::types::TypeParamInfo) -> bool {
278 info.constraint
279 .map(|c| self.visit_type(self._db, c))
280 .unwrap_or(false)
281 }
282
283 fn default_output() -> bool {
284 false
285 }
286}
287
288struct PrimitiveClassVisitor;
290
291impl TypeVisitor for PrimitiveClassVisitor {
292 type Output = Option<PrimitiveClass>;
293
294 fn visit_intrinsic(&mut self, kind: IntrinsicKind) -> Self::Output {
295 match kind {
296 IntrinsicKind::String => Some(PrimitiveClass::String),
297 IntrinsicKind::Number => Some(PrimitiveClass::Number),
298 IntrinsicKind::Boolean => Some(PrimitiveClass::Boolean),
299 IntrinsicKind::Bigint => Some(PrimitiveClass::Bigint),
300 IntrinsicKind::Symbol => Some(PrimitiveClass::Symbol),
301 IntrinsicKind::Null => Some(PrimitiveClass::Null),
302 IntrinsicKind::Undefined | IntrinsicKind::Void => Some(PrimitiveClass::Undefined),
303 _ => None,
304 }
305 }
306
307 fn visit_literal(&mut self, value: &LiteralValue) -> Self::Output {
308 match value {
309 LiteralValue::String(_) => Some(PrimitiveClass::String),
310 LiteralValue::Number(_) => Some(PrimitiveClass::Number),
311 LiteralValue::Boolean(_) => Some(PrimitiveClass::Boolean),
312 LiteralValue::BigInt(_) => Some(PrimitiveClass::Bigint),
313 }
314 }
315
316 fn visit_template_literal(&mut self, _template_id: u32) -> Self::Output {
317 Some(PrimitiveClass::String)
318 }
319
320 fn visit_unique_symbol(&mut self, _symbol_ref: u32) -> Self::Output {
321 Some(PrimitiveClass::Symbol)
322 }
323
324 fn default_output() -> Self::Output {
325 None
326 }
327}
328
329const fn intrinsic_overlaps_literal(kind: IntrinsicKind, value: &LiteralValue) -> bool {
332 matches!(
333 (kind, value),
334 (IntrinsicKind::String, LiteralValue::String(_))
335 | (IntrinsicKind::Number, LiteralValue::Number(_))
336 | (IntrinsicKind::Boolean, LiteralValue::Boolean(_))
337 | (IntrinsicKind::Bigint, LiteralValue::BigInt(_))
338 )
339}
340
341struct OverlapChecker<'a> {
343 db: &'a dyn TypeDatabase,
344 left: TypeId,
345}
346
347impl<'a> OverlapChecker<'a> {
348 fn new(db: &'a dyn TypeDatabase, left: TypeId) -> Self {
349 Self { db, left }
350 }
351
352 fn check(&mut self, right: TypeId) -> bool {
353 if self.left == right {
355 return true;
356 }
357
358 if matches!(
360 (self.left, right),
361 (TypeId::ANY | TypeId::UNKNOWN | TypeId::ERROR, _)
362 | (_, TypeId::ANY | TypeId::UNKNOWN | TypeId::ERROR)
363 ) {
364 return true;
365 }
366
367 if self.left == TypeId::NEVER || right == TypeId::NEVER {
368 return false;
369 }
370
371 if self.db.intersection2(self.left, right) == TypeId::NEVER {
373 return false;
374 }
375
376 self.visit_type(self.db, right)
378 }
379}
380
381impl<'a> TypeVisitor for OverlapChecker<'a> {
382 type Output = bool;
383
384 fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
385 true
387 }
388
389 fn visit_union(&mut self, list_id: u32) -> Self::Output {
390 let members = self.db.type_list(TypeListId(list_id));
391 members.iter().any(|&member| self.check(member))
392 }
393
394 fn visit_type_parameter(&mut self, info: &crate::types::TypeParamInfo) -> Self::Output {
395 match info.constraint {
397 Some(constraint) => self.check(constraint),
398 None => panic!("TypeParameter without constraint should not reach visitor"),
399 }
400 }
401
402 fn visit_infer(&mut self, info: &crate::types::TypeParamInfo) -> Self::Output {
403 match info.constraint {
405 Some(constraint) => self.check(constraint),
406 None => panic!("Infer without constraint should not reach visitor"),
407 }
408 }
409
410 fn visit_literal(&mut self, value: &LiteralValue) -> Self::Output {
411 match self.db.lookup(self.left) {
413 Some(TypeData::Literal(left_lit)) => left_lit == *value,
414 Some(TypeData::Union(members)) => {
415 let members = self.db.type_list(members);
417 members.iter().any(|&m| match self.db.lookup(m) {
418 Some(TypeData::Literal(lit)) => lit == *value,
419 Some(TypeData::Intrinsic(kind)) => intrinsic_overlaps_literal(kind, value),
420 _ => false,
421 })
422 }
423 Some(TypeData::Intrinsic(kind)) => intrinsic_overlaps_literal(kind, value),
426 Some(TypeData::Intersection(members)) => {
429 let members = self.db.type_list(members);
430 members.iter().any(|&m| match self.db.lookup(m) {
431 Some(TypeData::Literal(lit)) => lit == *value,
432 Some(TypeData::Intrinsic(kind)) => intrinsic_overlaps_literal(kind, value),
433 _ => false,
434 })
435 }
436 _ => false,
437 }
438 }
439
440 fn default_output() -> Self::Output {
441 true
445 }
446}
447
448pub struct BinaryOpEvaluator<'a> {
450 interner: &'a dyn QueryDatabase,
451}
452
453impl<'a> BinaryOpEvaluator<'a> {
454 pub fn new(interner: &'a dyn QueryDatabase) -> Self {
456 Self { interner }
457 }
458
459 pub fn is_valid_instanceof_left_operand(&self, type_id: TypeId) -> bool {
462 if type_id == TypeId::ERROR || type_id == TypeId::ANY || type_id == TypeId::UNKNOWN {
463 return true;
464 }
465 let mut visitor = InstanceofLeftOperandVisitor { _db: self.interner };
466 visitor.visit_type(self.interner, type_id)
467 }
468
469 pub fn is_valid_instanceof_right_operand<F>(
472 &self,
473 type_id: TypeId,
474 func_ty: TypeId,
475 assignable_check: &mut F,
476 ) -> bool
477 where
478 F: FnMut(TypeId, TypeId) -> bool,
479 {
480 if type_id == TypeId::ANY
481 || type_id == TypeId::UNKNOWN
482 || type_id == TypeId::ERROR
483 || type_id == TypeId::FUNCTION
484 {
485 return true;
486 }
487
488 if let Some(crate::TypeData::Union(list_id)) = self.interner.lookup(type_id) {
489 let members = self.interner.type_list(list_id);
490 let mut all_valid = true;
491 for &m in members.iter() {
492 if !self.is_valid_instanceof_right_operand(m, func_ty, assignable_check) {
493 all_valid = false;
494 break;
495 }
496 }
497 return all_valid && !members.is_empty();
498 }
499
500 if let Some(crate::TypeData::Intersection(list_id)) = self.interner.lookup(type_id) {
501 let members = self.interner.type_list(list_id);
502 let mut any_valid = false;
503 for &m in members.iter() {
504 if self.is_valid_instanceof_right_operand(m, func_ty, assignable_check) {
505 any_valid = true;
506 break;
507 }
508 }
509 return any_valid;
510 }
511
512 assignable_check(type_id, func_ty) || assignable_check(type_id, TypeId::FUNCTION)
513 }
514
515 pub fn is_arithmetic_operand(&self, type_id: TypeId) -> bool {
522 if type_id == TypeId::ANY
525 || type_id == TypeId::ERROR
526 || type_id == TypeId::UNKNOWN
527 || type_id == TypeId::NEVER
528 {
529 return true;
530 }
531 self.is_number_like(type_id) || self.is_bigint_like(type_id)
532 }
533
534 pub fn evaluate(&self, left: TypeId, right: TypeId, op: &'static str) -> BinaryOpResult {
536 match op {
537 "+" => self.evaluate_plus(left, right),
538 "-" | "*" | "/" | "%" | "**" | "&" | "|" | "^" | "<<" | ">>" | ">>>" => {
539 self.evaluate_arithmetic(left, right, op)
540 }
541 "==" | "!=" | "===" | "!==" => {
542 if self.has_overlap(left, right) {
543 BinaryOpResult::Success(TypeId::BOOLEAN)
544 } else {
545 BinaryOpResult::TypeError { left, right, op }
546 }
547 }
548 "<" | ">" | "<=" | ">=" => self.evaluate_comparison(left, right),
549 "&&" | "||" | "??" => self.evaluate_logical(left, right, op),
550 _ => BinaryOpResult::TypeError { left, right, op },
551 }
552 }
553
554 fn evaluate_plus(&self, left: TypeId, right: TypeId) -> BinaryOpResult {
556 if left == TypeId::UNKNOWN || right == TypeId::UNKNOWN {
558 return BinaryOpResult::Success(TypeId::UNKNOWN);
559 }
560
561 let left = if left == TypeId::ERROR {
564 TypeId::ANY
565 } else {
566 left
567 };
568 let right = if right == TypeId::ERROR {
569 TypeId::ANY
570 } else {
571 right
572 };
573
574 if self.is_symbol_like(left) || self.is_symbol_like(right) {
576 return BinaryOpResult::TypeError {
577 left,
578 right,
579 op: "+",
580 };
581 }
582
583 if left == TypeId::ANY || right == TypeId::ANY {
585 return BinaryOpResult::Success(TypeId::ANY);
586 }
587
588 if self.is_string_like(left) || self.is_string_like(right) {
590 let valid_left = self.is_string_like(left) || self.is_valid_string_concat_operand(left);
592 let valid_right =
593 self.is_string_like(right) || self.is_valid_string_concat_operand(right);
594
595 if valid_left && valid_right {
596 return BinaryOpResult::Success(TypeId::STRING);
597 }
598 return BinaryOpResult::TypeError {
600 left,
601 right,
602 op: "+",
603 };
604 }
605
606 if self.is_number_like(left) && self.is_number_like(right) {
608 return BinaryOpResult::Success(TypeId::NUMBER);
609 }
610
611 if self.is_bigint_like(left) && self.is_bigint_like(right) {
613 return BinaryOpResult::Success(TypeId::BIGINT);
614 }
615
616 BinaryOpResult::TypeError {
617 left,
618 right,
619 op: "+",
620 }
621 }
622
623 fn evaluate_arithmetic(&self, left: TypeId, right: TypeId, op: &'static str) -> BinaryOpResult {
625 if left == TypeId::UNKNOWN || right == TypeId::UNKNOWN {
627 return BinaryOpResult::Success(TypeId::UNKNOWN);
628 }
629
630 let left = if left == TypeId::ERROR {
632 TypeId::ANY
633 } else {
634 left
635 };
636 let right = if right == TypeId::ERROR {
637 TypeId::ANY
638 } else {
639 right
640 };
641
642 if self.is_symbol_like(left) || self.is_symbol_like(right) {
644 return BinaryOpResult::TypeError { left, right, op };
645 }
646
647 if left == TypeId::ANY || right == TypeId::ANY {
649 return BinaryOpResult::Success(TypeId::NUMBER);
650 }
651
652 if self.is_number_like(left) && self.is_number_like(right) {
654 return BinaryOpResult::Success(TypeId::NUMBER);
655 }
656
657 if self.is_bigint_like(left) && self.is_bigint_like(right) {
659 return BinaryOpResult::Success(TypeId::BIGINT);
660 }
661
662 BinaryOpResult::TypeError { left, right, op }
663 }
664
665 fn evaluate_comparison(&self, left: TypeId, right: TypeId) -> BinaryOpResult {
667 if left == TypeId::UNKNOWN || right == TypeId::UNKNOWN {
669 return BinaryOpResult::Success(TypeId::BOOLEAN);
670 }
671
672 let left = if left == TypeId::ERROR {
674 TypeId::ANY
675 } else {
676 left
677 };
678 let right = if right == TypeId::ERROR {
679 TypeId::ANY
680 } else {
681 right
682 };
683
684 if self.is_symbol_like(left) || self.is_symbol_like(right) {
686 return BinaryOpResult::TypeError {
687 left,
688 right,
689 op: "<",
690 };
691 }
692
693 if left == TypeId::ANY || right == TypeId::ANY {
695 return BinaryOpResult::Success(TypeId::BOOLEAN);
696 }
697
698 if self.is_number_like(left) && self.is_number_like(right) {
700 return BinaryOpResult::Success(TypeId::BOOLEAN);
701 }
702
703 if self.is_string_like(left) && self.is_string_like(right) {
705 return BinaryOpResult::Success(TypeId::BOOLEAN);
706 }
707
708 if self.is_bigint_like(left) && self.is_bigint_like(right) {
710 return BinaryOpResult::Success(TypeId::BOOLEAN);
711 }
712
713 if self.is_boolean_like(left) && self.is_boolean_like(right) {
715 return BinaryOpResult::Success(TypeId::BOOLEAN);
716 }
717
718 if self.is_orderable(left) && self.is_orderable(right) {
722 return BinaryOpResult::Success(TypeId::BOOLEAN);
723 }
724
725 BinaryOpResult::TypeError {
727 left,
728 right,
729 op: "<",
730 }
731 }
732
733 fn evaluate_logical(&self, left: TypeId, right: TypeId, op: &'static str) -> BinaryOpResult {
735 let ctx = crate::narrowing::NarrowingContext::new(self.interner);
736
737 let result = if op == "&&" {
738 let falsy_left = ctx.narrow_to_falsy(left);
740 let truthy_left = ctx.narrow_by_truthiness(left);
741
742 if truthy_left == TypeId::NEVER {
743 left
744 } else if falsy_left == TypeId::NEVER {
745 right
746 } else {
747 self.interner.union2(falsy_left, right)
748 }
749 } else if op == "||" {
750 let truthy_left = ctx.narrow_by_truthiness(left);
752 let falsy_left = ctx.narrow_to_falsy(left);
753
754 if falsy_left == TypeId::NEVER {
755 left
756 } else if truthy_left == TypeId::NEVER {
757 right
758 } else {
759 self.interner.union2(truthy_left, right)
760 }
761 } else {
762 let non_nullish_left = ctx.narrow_by_nullishness(left, false);
764 let nullish_left = ctx.narrow_by_nullishness(left, true);
765
766 if nullish_left == TypeId::NEVER {
767 left
768 } else if non_nullish_left == TypeId::NEVER {
769 right
770 } else {
771 self.interner.union2(non_nullish_left, right)
772 }
773 };
774
775 BinaryOpResult::Success(result)
776 }
777
778 fn is_number_like(&self, type_id: TypeId) -> bool {
780 if type_id == TypeId::NUMBER || type_id == TypeId::ANY {
781 return true;
782 }
783 let mut visitor = NumberLikeVisitor { _db: self.interner };
784 visitor.visit_type(self.interner, type_id)
785 }
786
787 fn is_string_like(&self, type_id: TypeId) -> bool {
789 if type_id == TypeId::STRING || type_id == TypeId::ANY {
790 return true;
791 }
792 let mut visitor = StringLikeVisitor { _db: self.interner };
793 visitor.visit_type(self.interner, type_id)
794 }
795
796 fn is_bigint_like(&self, type_id: TypeId) -> bool {
798 if type_id == TypeId::BIGINT || type_id == TypeId::ANY {
799 return true;
800 }
801 let mut visitor = BigIntLikeVisitor { _db: self.interner };
802 visitor.visit_type(self.interner, type_id)
803 }
804
805 fn is_orderable(&self, type_id: TypeId) -> bool {
809 if type_id == TypeId::ANY
810 || type_id == TypeId::NUMBER
811 || type_id == TypeId::STRING
812 || type_id == TypeId::BIGINT
813 {
814 return true;
815 }
816 let mut visitor = OrderableVisitor { _db: self.interner };
817 visitor.visit_type(self.interner, type_id)
818 }
819
820 pub fn has_overlap(&self, left: TypeId, right: TypeId) -> bool {
822 if left == right {
823 return true;
824 }
825 if left == TypeId::ANY
826 || right == TypeId::ANY
827 || left == TypeId::UNKNOWN
828 || right == TypeId::UNKNOWN
829 || left == TypeId::ERROR
830 || right == TypeId::ERROR
831 {
832 return true;
833 }
834 if left == TypeId::NEVER || right == TypeId::NEVER {
835 return false;
836 }
837
838 if let Some(TypeData::TypeParameter(info) | TypeData::Infer(info)) =
840 self.interner.lookup(left)
841 {
842 if let Some(constraint) = info.constraint {
843 return self.has_overlap(constraint, right);
844 }
845 return true;
846 }
847
848 if let Some(TypeData::TypeParameter(info) | TypeData::Infer(info)) =
849 self.interner.lookup(right)
850 {
851 if let Some(constraint) = info.constraint {
852 return self.has_overlap(left, constraint);
853 }
854 return true;
855 }
856
857 if let Some(TypeData::Union(members)) = self.interner.lookup(left) {
859 let members = self.interner.type_list(members);
860 return members
861 .iter()
862 .any(|member| self.has_overlap(*member, right));
863 }
864
865 if let Some(TypeData::Union(members)) = self.interner.lookup(right) {
866 let members = self.interner.type_list(members);
867 return members.iter().any(|member| self.has_overlap(left, *member));
868 }
869
870 if self.primitive_classes_disjoint(left, right) {
872 return false;
873 }
874
875 if self.interner.intersection2(left, right) == TypeId::NEVER {
877 return false;
878 }
879
880 let mut checker = OverlapChecker::new(self.interner, left);
882 checker.check(right)
883 }
884
885 fn primitive_classes_disjoint(&self, left: TypeId, right: TypeId) -> bool {
887 match (self.primitive_class(left), self.primitive_class(right)) {
888 (Some(left_class), Some(right_class)) => left_class != right_class,
889 _ => false,
890 }
891 }
892
893 fn primitive_class(&self, type_id: TypeId) -> Option<PrimitiveClass> {
895 let mut visitor = PrimitiveClassVisitor;
896 visitor.visit_type(self.interner, type_id)
897 }
898
899 pub fn is_symbol_like(&self, type_id: TypeId) -> bool {
901 if type_id == TypeId::SYMBOL {
902 return true;
903 }
904 let mut visitor = SymbolLikeVisitor { _db: self.interner };
905 visitor.visit_type(self.interner, type_id)
906 }
907
908 pub fn is_valid_computed_property_name_type(&self, type_id: TypeId) -> bool {
914 if type_id == TypeId::ANY || type_id == TypeId::NEVER || type_id == TypeId::ERROR {
915 return true;
916 }
917 if let Some(TypeData::Union(list_id)) = self.interner.lookup(type_id) {
919 let members = self.interner.type_list(list_id);
920 return !members.is_empty()
921 && members
922 .iter()
923 .all(|&m| self.is_valid_computed_property_name_type(m));
924 }
925 self.is_string_like(type_id) || self.is_number_like(type_id) || self.is_symbol_like(type_id)
926 }
927
928 pub fn is_boolean_like(&self, type_id: TypeId) -> bool {
930 if type_id == TypeId::BOOLEAN || type_id == TypeId::ANY {
931 return true;
932 }
933 let mut visitor = BooleanLikeVisitor { _db: self.interner };
934 visitor.visit_type(self.interner, type_id)
935 }
936
937 fn is_valid_string_concat_operand(&self, type_id: TypeId) -> bool {
940 if type_id == TypeId::ANY || type_id == TypeId::ERROR || type_id == TypeId::NEVER {
941 return true;
942 }
943 if type_id == TypeId::UNKNOWN {
944 return false;
945 }
946 if let Some(TypeData::Union(list_id)) = self.interner.lookup(type_id) {
947 let members = self.interner.type_list(list_id);
948 return !members.is_empty()
949 && members
950 .iter()
951 .all(|&member| self.is_valid_string_concat_operand(member));
952 }
953 if self.is_symbol_like(type_id) {
954 return false;
955 }
956 if self.is_number_like(type_id)
958 || self.is_boolean_like(type_id)
959 || self.is_bigint_like(type_id)
960 || type_id == TypeId::NULL
961 || type_id == TypeId::UNDEFINED
962 || type_id == TypeId::VOID
963 {
964 return true;
965 }
966
967 true
969 }
970}