1use crate::TypeDatabase;
52use crate::evaluation::evaluate::TypeEvaluator;
53use crate::objects::index_signatures::IndexKind;
54use crate::relations::subtype::{SubtypeChecker, TypeEnvironment};
55use crate::types::{
56 CallSignature, IntrinsicKind, LiteralValue, ParamInfo, TypeData, TypeId, TypeParamInfo,
57};
58#[cfg(test)]
59use crate::types::{FunctionShape, PropertyInfo, Visibility};
60use rustc_hash::FxHashMap;
61use std::cell::RefCell;
62use std::sync::Arc;
63use tsz_common::interner::Atom;
64
65#[derive(Clone, Debug, PartialEq, Eq)]
73pub enum IterableKind {
74 Array(TypeId),
76 Tuple(Vec<TypeId>),
78 String,
80 SyncIterator {
82 iterator_type: TypeId,
84 element_type: TypeId,
86 },
87 AsyncIterator {
89 iterator_type: TypeId,
91 element_type: TypeId,
93 },
94 NotIterable,
96}
97
98#[derive(Clone, Debug, PartialEq, Eq)]
100pub enum CallableKind {
101 Function {
103 params: Vec<ParamInfo>,
104 return_type: TypeId,
105 type_params: Vec<TypeParamInfo>,
106 },
107 Constructor {
109 params: Vec<ParamInfo>,
110 return_type: TypeId,
111 type_params: Vec<TypeParamInfo>,
112 },
113 Overloaded {
115 call_signatures: Vec<CallSignature>,
116 construct_signatures: Vec<CallSignature>,
117 },
118 NotCallable,
120}
121
122bitflags::bitflags! {
123 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
125 pub struct PrimitiveFlags: u32 {
126 const STRING_LIKE = 1 << 0;
127 const NUMBER_LIKE = 1 << 1;
128 const BOOLEAN_LIKE = 1 << 2;
129 const BIGINT_LIKE = 1 << 3;
130 const SYMBOL_LIKE = 1 << 4;
131 const VOID_LIKE = 1 << 5;
132 const NULLABLE = 1 << 6;
133 const UNDEFINED = 1 << 7;
134 const NULL = 1 << 8;
135 const NEVER = 1 << 9;
136 const UNKNOWN = 1 << 10;
137 const ANY = 1 << 11;
138 }
139}
140
141#[derive(Clone, Copy, Debug, PartialEq, Eq)]
143pub enum TruthinessKind {
144 AlwaysTruthy,
146 AlwaysFalsy,
148 Sometimes,
150 Unknown,
152}
153
154#[derive(Clone, Debug, PartialEq, Eq)]
156pub enum PropertyResult {
157 Found {
159 type_id: TypeId,
160 optional: bool,
161 readonly: bool,
162 },
163 NotFound,
165 IndexSignature { value_type: TypeId, readonly: bool },
167 IsAny,
169 IsUnknown,
171 IsError,
173}
174
175#[derive(Clone, Debug, PartialEq, Eq, Hash)]
184pub struct JudgeConfig {
185 pub strict_null_checks: bool,
187 pub strict_function_types: bool,
189 pub exact_optional_property_types: bool,
191 pub no_unchecked_indexed_access: bool,
193 pub sound_mode: bool,
195}
196
197impl Default for JudgeConfig {
198 fn default() -> Self {
199 Self {
200 strict_null_checks: true,
201 strict_function_types: true,
202 exact_optional_property_types: false,
203 no_unchecked_indexed_access: false,
204 sound_mode: false,
205 }
206 }
207}
208
209pub trait Judge {
231 fn is_subtype(&self, source: TypeId, target: TypeId) -> bool;
245
246 fn are_identical(&self, a: TypeId, b: TypeId) -> bool {
250 a == b || (self.is_subtype(a, b) && self.is_subtype(b, a))
251 }
252
253 fn evaluate(&self, type_id: TypeId) -> TypeId;
264
265 fn instantiate(&self, generic: TypeId, args: &[TypeId]) -> TypeId;
273
274 fn classify_iterable(&self, type_id: TypeId) -> IterableKind;
285
286 fn classify_callable(&self, type_id: TypeId) -> CallableKind;
293
294 fn classify_primitive(&self, type_id: TypeId) -> PrimitiveFlags;
300
301 fn classify_truthiness(&self, type_id: TypeId) -> TruthinessKind;
307
308 fn apparent_type(&self, type_id: TypeId) -> TypeId;
314
315 fn get_property(&self, type_id: TypeId, name: Atom) -> PropertyResult;
323
324 fn get_members(&self, type_id: TypeId) -> Arc<Vec<(Atom, TypeId)>>;
326
327 fn get_call_signatures(&self, type_id: TypeId) -> Arc<Vec<CallSignature>>;
329
330 fn get_construct_signatures(&self, type_id: TypeId) -> Arc<Vec<CallSignature>>;
332
333 fn get_index_type(&self, object: TypeId, key: TypeId) -> TypeId;
335
336 fn get_index_signature(&self, type_id: TypeId, kind: IndexKind) -> Option<TypeId>;
338
339 fn get_keyof(&self, type_id: TypeId) -> TypeId;
341
342 fn config(&self) -> &JudgeConfig;
348}
349
350pub struct DefaultJudge<'a> {
359 db: &'a dyn TypeDatabase,
360 config: JudgeConfig,
361 env: &'a TypeEnvironment,
363 subtype_cache: RefCell<FxHashMap<(TypeId, TypeId), bool>>,
365 eval_cache: RefCell<FxHashMap<TypeId, TypeId>>,
367}
368
369impl<'a> DefaultJudge<'a> {
370 pub fn new(db: &'a dyn TypeDatabase, env: &'a TypeEnvironment, config: JudgeConfig) -> Self {
372 DefaultJudge {
373 db,
374 config,
375 env,
376 subtype_cache: RefCell::new(FxHashMap::default()),
377 eval_cache: RefCell::new(FxHashMap::default()),
378 }
379 }
380
381 pub fn with_defaults(db: &'a dyn TypeDatabase, env: &'a TypeEnvironment) -> Self {
383 Self::new(db, env, JudgeConfig::default())
384 }
385
386 pub fn clear_caches(&self) {
388 self.subtype_cache.borrow_mut().clear();
389 self.eval_cache.borrow_mut().clear();
390 }
391
392 pub fn db(&self) -> &'a dyn TypeDatabase {
394 self.db
395 }
396}
397
398impl<'a> Judge for DefaultJudge<'a> {
399 fn is_subtype(&self, source: TypeId, target: TypeId) -> bool {
400 if source == target {
402 return true;
403 }
404
405 let key = (source, target);
407 if let Some(&cached) = self.subtype_cache.borrow().get(&key) {
408 return cached;
409 }
410
411 let mut checker = SubtypeChecker::with_resolver(self.db, self.env);
413 checker.strict_function_types = self.config.strict_function_types;
414 checker.strict_null_checks = self.config.strict_null_checks;
415 checker.exact_optional_property_types = self.config.exact_optional_property_types;
416 checker.no_unchecked_indexed_access = self.config.no_unchecked_indexed_access;
417
418 let result = checker.is_subtype_of(source, target);
419
420 self.subtype_cache.borrow_mut().insert(key, result);
422
423 result
424 }
425
426 fn evaluate(&self, type_id: TypeId) -> TypeId {
427 if type_id.is_intrinsic() {
429 return type_id;
430 }
431
432 if let Some(&cached) = self.eval_cache.borrow().get(&type_id) {
434 return cached;
435 }
436
437 let mut evaluator = TypeEvaluator::with_resolver(self.db, self.env);
439 let result = evaluator.evaluate(type_id);
440
441 self.eval_cache.borrow_mut().insert(type_id, result);
443
444 result
445 }
446
447 fn instantiate(&self, generic: TypeId, args: &[TypeId]) -> TypeId {
448 use crate::instantiation::instantiate::instantiate_generic;
449
450 let key = match self.db.lookup(generic) {
452 Some(k) => k,
453 None => return TypeId::ERROR,
454 };
455
456 if let TypeData::Lazy(def_id) = &key
458 && let Some(params) = self.env.get_def_params(*def_id)
459 && let Some(resolved) = self.env.get_def(*def_id)
460 {
461 return instantiate_generic(self.db, resolved, params, args);
462 }
463
464 generic
466 }
467
468 fn classify_iterable(&self, type_id: TypeId) -> IterableKind {
469 let evaluated = self.evaluate(type_id);
470
471 if evaluated == TypeId::ANY || evaluated == TypeId::ERROR {
473 return IterableKind::NotIterable;
474 }
475 if evaluated == TypeId::STRING {
476 return IterableKind::String;
477 }
478
479 let key = match self.db.lookup(evaluated) {
480 Some(k) => k,
481 None => return IterableKind::NotIterable,
482 };
483
484 match key {
485 TypeData::Array(elem) => IterableKind::Array(elem),
486 TypeData::Tuple(list_id) => {
487 let elements = self.db.tuple_list(list_id);
488 let types: Vec<TypeId> = elements.iter().map(|e| e.type_id).collect();
489 IterableKind::Tuple(types)
490 }
491 TypeData::Literal(LiteralValue::String(_)) => IterableKind::String,
492 TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
493 let has_usable_iterator_signature = |method_type: TypeId| {
494 if method_type == TypeId::ANY
495 || method_type == TypeId::UNKNOWN
496 || method_type == TypeId::ERROR
497 {
498 return true;
499 }
500 self.get_call_signatures(method_type)
501 .iter()
502 .any(|sig| sig.params.iter().all(|param| param.optional || param.rest))
503 };
504
505 let shape = self.db.object_shape(shape_id);
507 let iterator_name = self.db.intern_string("[Symbol.iterator]");
508 for prop in &shape.properties {
509 if prop.name == iterator_name && has_usable_iterator_signature(prop.type_id) {
510 return IterableKind::SyncIterator {
512 iterator_type: prop.type_id,
513 element_type: self.extract_iterator_element_type(prop.type_id),
514 };
515 }
516 }
517 let async_iterator_name = self.db.intern_string("[Symbol.asyncIterator]");
519 for prop in &shape.properties {
520 if prop.name == async_iterator_name
521 && has_usable_iterator_signature(prop.type_id)
522 {
523 return IterableKind::AsyncIterator {
524 iterator_type: prop.type_id,
525 element_type: self.extract_iterator_element_type(prop.type_id),
526 };
527 }
528 }
529 IterableKind::NotIterable
530 }
531 TypeData::Union(members_id) => {
532 let members = self.db.type_list(members_id);
534 let mut element_types = Vec::new();
535 for &member in members.iter() {
536 match self.classify_iterable(member) {
537 IterableKind::Array(elem) => element_types.push(elem),
538 IterableKind::Tuple(elems) => element_types.extend(elems),
539 IterableKind::String => element_types.push(TypeId::STRING),
540 IterableKind::SyncIterator { element_type, .. }
541 | IterableKind::AsyncIterator { element_type, .. } => {
542 element_types.push(element_type);
543 }
544 IterableKind::NotIterable => return IterableKind::NotIterable,
545 }
546 }
547 if element_types.is_empty() {
548 IterableKind::NotIterable
549 } else {
550 let union = self.db.union(element_types);
551 IterableKind::Array(union)
552 }
553 }
554 _ => IterableKind::NotIterable,
555 }
556 }
557
558 fn classify_callable(&self, type_id: TypeId) -> CallableKind {
559 let evaluated = self.evaluate(type_id);
560
561 if evaluated == TypeId::ANY {
562 return CallableKind::NotCallable;
563 }
564
565 let key = match self.db.lookup(evaluated) {
566 Some(k) => k,
567 None => return CallableKind::NotCallable,
568 };
569
570 match key {
571 TypeData::Function(fn_id) => {
572 let shape = self.db.function_shape(fn_id);
573 if shape.is_constructor {
574 CallableKind::Constructor {
575 params: shape.params.clone(),
576 return_type: shape.return_type,
577 type_params: shape.type_params.clone(),
578 }
579 } else {
580 CallableKind::Function {
581 params: shape.params.clone(),
582 return_type: shape.return_type,
583 type_params: shape.type_params.clone(),
584 }
585 }
586 }
587 TypeData::Callable(callable_id) => {
588 let shape = self.db.callable_shape(callable_id);
589 CallableKind::Overloaded {
590 call_signatures: shape.call_signatures.clone(),
591 construct_signatures: shape.construct_signatures.clone(),
592 }
593 }
594 _ => CallableKind::NotCallable,
595 }
596 }
597
598 fn classify_primitive(&self, type_id: TypeId) -> PrimitiveFlags {
599 let mut flags = PrimitiveFlags::empty();
600
601 match type_id {
603 TypeId::ANY => return PrimitiveFlags::ANY,
604 TypeId::UNKNOWN => return PrimitiveFlags::UNKNOWN,
605 TypeId::NEVER => return PrimitiveFlags::NEVER,
606 TypeId::VOID => return PrimitiveFlags::VOID_LIKE,
607 TypeId::UNDEFINED => {
608 return PrimitiveFlags::UNDEFINED | PrimitiveFlags::NULLABLE;
609 }
610 TypeId::NULL => return PrimitiveFlags::NULL | PrimitiveFlags::NULLABLE,
611 TypeId::BOOLEAN | TypeId::BOOLEAN_TRUE | TypeId::BOOLEAN_FALSE => {
612 return PrimitiveFlags::BOOLEAN_LIKE;
613 }
614 TypeId::NUMBER => return PrimitiveFlags::NUMBER_LIKE,
615 TypeId::STRING => return PrimitiveFlags::STRING_LIKE,
616 TypeId::BIGINT => return PrimitiveFlags::BIGINT_LIKE,
617 TypeId::SYMBOL => return PrimitiveFlags::SYMBOL_LIKE,
618 _ => {}
619 }
620
621 let key = match self.db.lookup(type_id) {
622 Some(k) => k,
623 None => return flags,
624 };
625
626 match key {
627 TypeData::Literal(LiteralValue::String(_)) | TypeData::TemplateLiteral(_) => {
628 flags |= PrimitiveFlags::STRING_LIKE
629 }
630 TypeData::Literal(LiteralValue::Number(_)) => flags |= PrimitiveFlags::NUMBER_LIKE,
631 TypeData::Literal(LiteralValue::Boolean(_)) => flags |= PrimitiveFlags::BOOLEAN_LIKE,
632 TypeData::Literal(LiteralValue::BigInt(_)) => flags |= PrimitiveFlags::BIGINT_LIKE,
633 TypeData::Union(members_id) => {
634 let members = self.db.type_list(members_id);
635 for &member in members.iter() {
636 flags |= self.classify_primitive(member);
637 }
638 }
639 _ => {}
640 }
641
642 flags
643 }
644
645 fn classify_truthiness(&self, type_id: TypeId) -> TruthinessKind {
646 match type_id {
648 TypeId::ANY | TypeId::UNKNOWN => return TruthinessKind::Unknown,
649 TypeId::NEVER
650 | TypeId::VOID
651 | TypeId::UNDEFINED
652 | TypeId::NULL
653 | TypeId::BOOLEAN_FALSE => return TruthinessKind::AlwaysFalsy,
654 TypeId::BOOLEAN => return TruthinessKind::Sometimes,
655 TypeId::BOOLEAN_TRUE => return TruthinessKind::AlwaysTruthy,
656 _ => {}
657 }
658
659 let key = match self.db.lookup(type_id) {
660 Some(k) => k,
661 None => return TruthinessKind::Unknown,
662 };
663
664 match key {
665 TypeData::Literal(LiteralValue::String(s)) => {
666 let s_str = self.db.resolve_atom(s);
667 if s_str.is_empty() {
668 TruthinessKind::AlwaysFalsy
669 } else {
670 TruthinessKind::AlwaysTruthy
671 }
672 }
673 TypeData::Literal(LiteralValue::Number(n)) => {
674 if n.0 == 0.0 || n.0.is_nan() {
675 TruthinessKind::AlwaysFalsy
676 } else {
677 TruthinessKind::AlwaysTruthy
678 }
679 }
680 TypeData::Literal(LiteralValue::Boolean(b)) => {
681 if b {
682 TruthinessKind::AlwaysTruthy
683 } else {
684 TruthinessKind::AlwaysFalsy
685 }
686 }
687 TypeData::Literal(LiteralValue::BigInt(s)) => {
688 let s_str = self.db.resolve_atom(s);
689 if s_str == "0" || s_str == "0n" {
690 TruthinessKind::AlwaysFalsy
691 } else {
692 TruthinessKind::AlwaysTruthy
693 }
694 }
695 TypeData::Object(_)
696 | TypeData::ObjectWithIndex(_)
697 | TypeData::Array(_)
698 | TypeData::Tuple(_)
699 | TypeData::Function(_)
700 | TypeData::Callable(_) => TruthinessKind::AlwaysTruthy,
701 TypeData::Union(members_id) => {
702 let members = self.db.type_list(members_id);
703 let mut has_truthy = false;
704 let mut has_falsy = false;
705 for &member in members.iter() {
706 match self.classify_truthiness(member) {
707 TruthinessKind::AlwaysTruthy => has_truthy = true,
708 TruthinessKind::AlwaysFalsy => has_falsy = true,
709 TruthinessKind::Sometimes | TruthinessKind::Unknown => {
710 has_truthy = true;
711 has_falsy = true;
712 }
713 }
714 }
715 match (has_truthy, has_falsy) {
716 (true, true) => TruthinessKind::Sometimes,
717 (true, false) => TruthinessKind::AlwaysTruthy,
718 (false, true) => TruthinessKind::AlwaysFalsy,
719 (false, false) => TruthinessKind::Unknown,
720 }
721 }
722 TypeData::Intrinsic(
723 IntrinsicKind::String | IntrinsicKind::Number | IntrinsicKind::Bigint,
724 ) => {
725 TruthinessKind::Sometimes
727 }
728 _ => TruthinessKind::Unknown,
729 }
730 }
731
732 fn apparent_type(&self, type_id: TypeId) -> TypeId {
733 let key = match self.db.lookup(type_id) {
734 Some(k) => k,
735 None => return type_id,
736 };
737
738 match key {
739 TypeData::TypeParameter(ref info) => info.constraint.unwrap_or(type_id),
740 TypeData::Lazy(def_id) => self.env.get_def(def_id).unwrap_or(type_id),
741 _ => type_id,
742 }
743 }
744
745 fn get_property(&self, type_id: TypeId, name: Atom) -> PropertyResult {
746 match type_id {
748 TypeId::ANY => return PropertyResult::IsAny,
749 TypeId::UNKNOWN => return PropertyResult::IsUnknown,
750 TypeId::ERROR => return PropertyResult::IsError,
751 _ => {}
752 }
753
754 let evaluated = self.evaluate(type_id);
755 let key = match self.db.lookup(evaluated) {
756 Some(k) => k,
757 None => return PropertyResult::NotFound,
758 };
759
760 match key {
761 TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
762 let shape = self.db.object_shape(shape_id);
763
764 for prop in &shape.properties {
766 if prop.name == name {
767 return PropertyResult::Found {
768 type_id: prop.type_id,
769 optional: prop.optional,
770 readonly: prop.readonly,
771 };
772 }
773 }
774
775 if let Some(ref string_idx) = shape.string_index {
777 return PropertyResult::IndexSignature {
778 value_type: string_idx.value_type,
779 readonly: string_idx.readonly,
780 };
781 }
782
783 PropertyResult::NotFound
784 }
785 TypeData::Array(_elem) => {
786 let name_str = self.db.resolve_atom(name);
787 if name_str == "length" {
788 PropertyResult::Found {
789 type_id: TypeId::NUMBER,
790 optional: false,
791 readonly: false,
792 }
793 } else {
794 PropertyResult::NotFound
796 }
797 }
798 TypeData::Tuple(list_id) => {
799 let name_str = self.db.resolve_atom(name);
800 if name_str == "length" {
801 let elements = self.db.tuple_list(list_id);
802 let len_type = self.db.literal_number(elements.len() as f64);
803 PropertyResult::Found {
804 type_id: len_type,
805 optional: false,
806 readonly: true,
807 }
808 } else if let Ok(idx) = name_str.parse::<usize>() {
809 let elements = self.db.tuple_list(list_id);
810 if let Some(elem) = elements.get(idx) {
811 PropertyResult::Found {
812 type_id: elem.type_id,
813 optional: elem.optional,
814 readonly: false,
815 }
816 } else {
817 PropertyResult::NotFound
818 }
819 } else {
820 PropertyResult::NotFound
821 }
822 }
823 TypeData::Union(members_id) => {
824 let members = self.db.type_list(members_id);
825 let mut result_types = Vec::new();
826 let mut all_optional = true;
827 let mut any_readonly = false;
828
829 for &member in members.iter() {
830 match self.get_property(member, name) {
831 PropertyResult::Found {
832 type_id,
833 optional,
834 readonly,
835 } => {
836 result_types.push(type_id);
837 if !optional {
838 all_optional = false;
839 }
840 if readonly {
841 any_readonly = true;
842 }
843 }
844 PropertyResult::IndexSignature {
845 value_type,
846 readonly,
847 } => {
848 result_types.push(value_type);
849 if readonly {
850 any_readonly = true;
851 }
852 }
853 PropertyResult::IsAny => return PropertyResult::IsAny,
854 PropertyResult::IsUnknown => return PropertyResult::IsUnknown,
855 PropertyResult::IsError => return PropertyResult::IsError,
856 PropertyResult::NotFound => {
857 return PropertyResult::NotFound;
859 }
860 }
861 }
862
863 if result_types.is_empty() {
864 PropertyResult::NotFound
865 } else {
866 PropertyResult::Found {
867 type_id: self.db.union(result_types),
868 optional: all_optional,
869 readonly: any_readonly,
870 }
871 }
872 }
873 TypeData::Intersection(members_id) => {
874 let members = self.db.type_list(members_id);
875 let mut found_types = Vec::new();
876 let mut optional = true;
877 let mut readonly = false;
878
879 for &member in members.iter() {
880 if let PropertyResult::Found {
881 type_id,
882 optional: opt,
883 readonly: ro,
884 } = self.get_property(member, name)
885 {
886 found_types.push(type_id);
887 if !opt {
888 optional = false;
889 }
890 if ro {
891 readonly = true;
892 }
893 }
894 }
895
896 if found_types.is_empty() {
897 PropertyResult::NotFound
898 } else {
899 PropertyResult::Found {
900 type_id: self.db.intersection(found_types),
901 optional,
902 readonly,
903 }
904 }
905 }
906 _ => PropertyResult::NotFound,
907 }
908 }
909
910 fn get_members(&self, type_id: TypeId) -> Arc<Vec<(Atom, TypeId)>> {
911 let evaluated = self.evaluate(type_id);
912 let key = match self.db.lookup(evaluated) {
913 Some(k) => k,
914 None => return Arc::new(Vec::new()),
915 };
916
917 match key {
918 TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
919 let shape = self.db.object_shape(shape_id);
920 Arc::new(
921 shape
922 .properties
923 .iter()
924 .map(|p| (p.name, p.type_id))
925 .collect(),
926 )
927 }
928 TypeData::Callable(callable_id) => {
929 let shape = self.db.callable_shape(callable_id);
930 Arc::new(
931 shape
932 .properties
933 .iter()
934 .map(|p| (p.name, p.type_id))
935 .collect(),
936 )
937 }
938 _ => Arc::new(Vec::new()),
939 }
940 }
941
942 fn get_call_signatures(&self, type_id: TypeId) -> Arc<Vec<CallSignature>> {
943 let evaluated = self.evaluate(type_id);
944 let key = match self.db.lookup(evaluated) {
945 Some(k) => k,
946 None => return Arc::new(Vec::new()),
947 };
948
949 match key {
950 TypeData::Function(fn_id) => {
951 let shape = self.db.function_shape(fn_id);
952 if shape.is_constructor {
953 return Arc::new(Vec::new());
954 }
955 Arc::new(vec![CallSignature {
956 type_params: shape.type_params.clone(),
957 params: shape.params.clone(),
958 this_type: shape.this_type,
959 return_type: shape.return_type,
960 type_predicate: shape.type_predicate.clone(),
961 is_method: shape.is_method,
962 }])
963 }
964 TypeData::Callable(callable_id) => {
965 let shape = self.db.callable_shape(callable_id);
966 Arc::new(shape.call_signatures.clone())
967 }
968 _ => Arc::new(Vec::new()),
969 }
970 }
971
972 fn get_construct_signatures(&self, type_id: TypeId) -> Arc<Vec<CallSignature>> {
973 let evaluated = self.evaluate(type_id);
974 let key = match self.db.lookup(evaluated) {
975 Some(k) => k,
976 None => return Arc::new(Vec::new()),
977 };
978
979 match key {
980 TypeData::Function(fn_id) => {
981 let shape = self.db.function_shape(fn_id);
982 if !shape.is_constructor {
983 return Arc::new(Vec::new());
984 }
985 Arc::new(vec![CallSignature {
986 type_params: shape.type_params.clone(),
987 params: shape.params.clone(),
988 this_type: shape.this_type,
989 return_type: shape.return_type,
990 type_predicate: shape.type_predicate.clone(),
991 is_method: false,
992 }])
993 }
994 TypeData::Callable(callable_id) => {
995 let shape = self.db.callable_shape(callable_id);
996 Arc::new(shape.construct_signatures.clone())
997 }
998 _ => Arc::new(Vec::new()),
999 }
1000 }
1001
1002 fn get_index_type(&self, object: TypeId, key: TypeId) -> TypeId {
1003 crate::evaluation::evaluate::evaluate_index_access_with_options(
1004 self.db,
1005 object,
1006 key,
1007 self.config.no_unchecked_indexed_access,
1008 )
1009 }
1010
1011 fn get_index_signature(&self, type_id: TypeId, kind: IndexKind) -> Option<TypeId> {
1012 let evaluated = self.evaluate(type_id);
1013 let key = self.db.lookup(evaluated)?;
1014
1015 match key {
1016 TypeData::ObjectWithIndex(shape_id) => {
1017 let shape = self.db.object_shape(shape_id);
1018 match kind {
1019 IndexKind::String => shape.string_index.as_ref().map(|s| s.value_type),
1020 IndexKind::Number => shape.number_index.as_ref().map(|s| s.value_type),
1021 }
1022 }
1023 TypeData::Array(elem) => (kind == IndexKind::Number).then_some(elem),
1024 TypeData::Tuple(list_id) => (kind == IndexKind::Number).then(|| {
1025 let elements = self.db.tuple_list(list_id);
1026 let types: Vec<TypeId> = elements.iter().map(|e| e.type_id).collect();
1027 self.db.union(types)
1028 }),
1029 _ => None,
1030 }
1031 }
1032
1033 fn get_keyof(&self, type_id: TypeId) -> TypeId {
1034 crate::evaluation::evaluate::evaluate_keyof(self.db, type_id)
1035 }
1036
1037 fn config(&self) -> &JudgeConfig {
1038 &self.config
1039 }
1040}
1041
1042impl<'a> DefaultJudge<'a> {
1043 fn extract_iterator_element_type(&self, iterator_type: TypeId) -> TypeId {
1045 let next_name = self.db.intern_string("next");
1047 if let PropertyResult::Found { type_id, .. } = self.get_property(iterator_type, next_name) {
1048 if let Some(TypeData::Function(fn_id)) = self.db.lookup(type_id) {
1050 let shape = self.db.function_shape(fn_id);
1051 let value_name = self.db.intern_string("value");
1053 if let PropertyResult::Found {
1054 type_id: value_type,
1055 ..
1056 } = self.get_property(shape.return_type, value_name)
1057 {
1058 return value_type;
1059 }
1060 }
1061 }
1062 TypeId::UNKNOWN
1063 }
1064}
1065
1066#[cfg(test)]
1071#[path = "../../tests/judge_tests.rs"]
1072mod tests;