1use crate::{QueryDatabase, TypeData, TypeDatabase, TypeId};
31use tsz_common::Atom;
32
33pub use crate::type_queries_classifiers::{
35 AssignabilityEvalKind, AugmentationTargetKind, BindingElementTypeKind, ConstructorAccessKind,
36 ExcessPropertiesKind, InterfaceMergeKind, SymbolResolutionTraversalKind,
37 classify_for_assignability_eval, classify_for_augmentation, classify_for_binding_element,
38 classify_for_constructor_access, classify_for_excess_properties, classify_for_interface_merge,
39 classify_for_symbol_resolution_traversal, get_conditional_type_id, get_def_id,
40 get_enum_components, get_keyof_inner, get_lazy_def_id, get_mapped_type_id, get_type_identity,
41};
42pub use crate::type_queries_extended::get_application_info;
43pub use crate::type_queries_extended::{
44 AbstractClassCheckKind, AbstractConstructorKind, ArrayLikeKind, BaseInstanceMergeKind,
45 CallSignaturesKind, ClassDeclTypeKind, ConstructorCheckKind, ConstructorReturnMergeKind,
46 ContextualLiteralAllowKind, ElementIndexableKind, IndexKeyKind, InstanceTypeKind,
47 KeyOfTypeKind, LazyTypeKind, LiteralKeyKind, LiteralTypeKind, MappedConstraintKind,
48 NamespaceMemberKind, PrivateBrandKind, PromiseTypeKind, PropertyAccessResolutionKind,
49 StringLiteralKeyKind, TypeArgumentExtractionKind, TypeParameterKind, TypeQueryKind,
50 TypeResolutionKind, classify_array_like, classify_element_indexable,
51 classify_for_abstract_check, classify_for_base_instance_merge, classify_for_call_signatures,
52 classify_for_class_decl, classify_for_constructor_check, classify_for_constructor_return_merge,
53 classify_for_contextual_literal, classify_for_instance_type, classify_for_lazy_resolution,
54 classify_for_private_brand, classify_for_property_access_resolution,
55 classify_for_string_literal_keys, classify_for_type_argument_extraction,
56 classify_for_type_resolution, classify_index_key, classify_literal_key, classify_literal_type,
57 classify_mapped_constraint, classify_namespace_member, classify_promise_type,
58 classify_type_parameter, classify_type_query, create_boolean_literal_type,
59 create_number_literal_type, create_string_literal_type, get_application_base,
60 get_boolean_literal_value, get_callable_type_param_count, get_literal_property_name,
61 get_number_literal_value, get_string_literal_atom, get_string_literal_value, get_tuple_list_id,
62 get_type_param_default, get_widened_literal_type, is_boolean_literal, is_direct_type_parameter,
63 is_invalid_index_type, is_number_literal, is_object_with_index_type, is_string_literal,
64 unwrap_readonly_for_lookup, widen_literal_to_primitive,
65};
66
67pub use crate::type_queries_data::*;
68pub use crate::type_queries_flow::*;
69
70pub fn get_keyof_type(db: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
71 get_keyof_inner(db, type_id)
72}
73
74pub fn get_allowed_keys(db: &dyn TypeDatabase, type_id: TypeId) -> rustc_hash::FxHashSet<String> {
75 let atoms = collect_property_name_atoms_for_diagnostics(db, type_id, 10);
76 atoms.into_iter().map(|a| db.resolve_atom(a)).collect()
77}
78
79pub fn is_callable_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
87 matches!(
88 db.lookup(type_id),
89 Some(TypeData::Callable(_) | TypeData::Function(_))
90 )
91}
92
93pub fn is_invokable_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
121 match db.lookup(type_id) {
122 Some(TypeData::Function(_)) => true,
123 Some(TypeData::Callable(shape_id)) => {
124 let shape = db.callable_shape(shape_id);
125 !shape.call_signatures.is_empty()
127 }
128 Some(TypeData::Intersection(list_id)) => {
130 let members = db.type_list(list_id);
131 members.iter().any(|&m| is_invokable_type(db, m))
132 }
133 _ => false,
134 }
135}
136
137pub fn is_tuple_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
141 matches!(db.lookup(type_id), Some(TypeData::Tuple(_)))
142}
143
144pub fn is_union_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
148 matches!(db.lookup(type_id), Some(TypeData::Union(_)))
149}
150
151pub fn is_intersection_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
155 matches!(db.lookup(type_id), Some(TypeData::Intersection(_)))
156}
157
158pub fn is_object_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
162 matches!(
163 db.lookup(type_id),
164 Some(TypeData::Object(_) | TypeData::ObjectWithIndex(_))
165 )
166}
167
168pub fn is_array_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
172 matches!(db.lookup(type_id), Some(TypeData::Array(_)))
173}
174
175pub fn is_literal_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
179 matches!(db.lookup(type_id), Some(TypeData::Literal(_)))
180}
181
182pub fn is_generic_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
186 matches!(db.lookup(type_id), Some(TypeData::Application(_)))
187}
188
189pub fn is_type_reference(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
193 matches!(
194 db.lookup(type_id),
195 Some(TypeData::Lazy(_) | TypeData::Recursive(_) | TypeData::BoundParameter(_))
196 )
197}
198
199pub fn is_conditional_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
203 matches!(db.lookup(type_id), Some(TypeData::Conditional(_)))
204}
205
206pub fn is_mapped_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
210 matches!(db.lookup(type_id), Some(TypeData::Mapped(_)))
211}
212
213pub fn is_template_literal_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
217 matches!(db.lookup(type_id), Some(TypeData::TemplateLiteral(_)))
218}
219
220pub fn is_uninstantiated_type_parameter(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
227 match db.lookup(type_id) {
228 Some(
229 TypeData::TypeParameter(_)
230 | TypeData::BoundParameter(_)
231 | TypeData::Infer(_)
232 | TypeData::Lazy(_),
233 ) => true,
234 Some(TypeData::Intersection(list_id) | TypeData::Union(list_id)) => {
235 let elements = db.type_list(list_id);
236 elements
237 .iter()
238 .any(|&e| is_uninstantiated_type_parameter(db, e))
239 }
240 _ => false,
241 }
242}
243
244pub fn is_type_parameter(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
250 matches!(
251 db.lookup(type_id),
252 Some(TypeData::TypeParameter(_) | TypeData::BoundParameter(_) | TypeData::Infer(_))
253 )
254}
255
256pub fn is_index_access_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
260 matches!(db.lookup(type_id), Some(TypeData::IndexAccess(_, _)))
261}
262
263pub fn is_keyof_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
267 matches!(db.lookup(type_id), Some(TypeData::KeyOf(_)))
268}
269
270pub fn is_type_query(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
274 matches!(db.lookup(type_id), Some(TypeData::TypeQuery(_)))
275}
276
277pub fn is_readonly_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
281 matches!(db.lookup(type_id), Some(TypeData::ReadonlyType(_)))
282}
283
284pub fn is_unique_symbol_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
288 matches!(db.lookup(type_id), Some(TypeData::UniqueSymbol(_)))
289}
290
291pub fn is_type_usable_as_property_name(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
296 if type_id == TypeId::ANY {
297 return true;
298 }
299 matches!(
300 db.lookup(type_id),
301 Some(
302 TypeData::Literal(crate::LiteralValue::String(_))
303 | TypeData::Literal(crate::LiteralValue::Number(_))
304 | TypeData::UniqueSymbol(_)
305 )
306 )
307}
308
309pub fn is_this_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
313 matches!(db.lookup(type_id), Some(TypeData::ThisType))
314}
315
316pub fn is_error_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
320 type_id == TypeId::ERROR || matches!(db.lookup(type_id), Some(TypeData::Error))
321}
322
323pub fn needs_evaluation_for_merge(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
330 matches!(
331 db.lookup(type_id),
332 Some(TypeData::Application(_) | TypeData::Lazy(_))
333 )
334}
335
336pub fn get_function_return_type(db: &dyn TypeDatabase, type_id: TypeId) -> TypeId {
340 match db.lookup(type_id) {
341 Some(TypeData::Function(shape_id)) => db.function_shape(shape_id).return_type,
342 _ => TypeId::ERROR,
343 }
344}
345
346pub fn get_function_parameter_types(db: &dyn TypeDatabase, type_id: TypeId) -> Vec<TypeId> {
350 match db.lookup(type_id) {
351 Some(TypeData::Function(shape_id)) => db
352 .function_shape(shape_id)
353 .params
354 .iter()
355 .map(|p| p.type_id)
356 .collect(),
357 _ => Vec::new(),
358 }
359}
360
361pub fn is_intrinsic_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
365 matches!(db.lookup(type_id), Some(TypeData::Intrinsic(_)))
366}
367
368pub fn is_primitive_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
372 if type_id.is_intrinsic() {
374 return true;
375 }
376 matches!(
377 db.lookup(type_id),
378 Some(TypeData::Intrinsic(_) | TypeData::Literal(_))
379 )
380}
381
382use crate::types::IntrinsicKind;
417
418macro_rules! define_intrinsic_check {
423 ($fn_name:ident, $type_id:ident, $kind:ident) => {
424 pub fn $fn_name(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
425 type_id == TypeId::$type_id
426 || matches!(
427 db.lookup(type_id),
428 Some(TypeData::Intrinsic(IntrinsicKind::$kind))
429 )
430 }
431 };
432}
433
434define_intrinsic_check!(is_any_type, ANY, Any);
435define_intrinsic_check!(is_unknown_type, UNKNOWN, Unknown);
436define_intrinsic_check!(is_never_type, NEVER, Never);
437define_intrinsic_check!(is_void_type, VOID, Void);
438define_intrinsic_check!(is_undefined_type, UNDEFINED, Undefined);
439define_intrinsic_check!(is_null_type, NULL, Null);
440define_intrinsic_check!(is_string_type, STRING, String);
441define_intrinsic_check!(is_number_type, NUMBER, Number);
442define_intrinsic_check!(is_bigint_type, BIGINT, Bigint);
443define_intrinsic_check!(is_boolean_type, BOOLEAN, Boolean);
444define_intrinsic_check!(is_symbol_type, SYMBOL, Symbol);
445
446pub fn is_object_like_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
454 is_object_like_type_impl(db, type_id)
455}
456
457fn is_object_like_type_impl(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
458 match db.lookup(type_id) {
459 Some(
460 TypeData::Object(_)
461 | TypeData::ObjectWithIndex(_)
462 | TypeData::Array(_)
463 | TypeData::Tuple(_)
464 | TypeData::Mapped(_)
465 | TypeData::Function(_)
466 | TypeData::Callable(_)
467 | TypeData::Intrinsic(IntrinsicKind::Object | IntrinsicKind::Function),
468 ) => true,
469 Some(TypeData::ReadonlyType(inner)) => is_object_like_type_impl(db, inner),
470 Some(TypeData::Intersection(members)) => {
471 let members = db.type_list(members);
472 members
473 .iter()
474 .all(|&member| is_object_like_type_impl(db, member))
475 }
476 Some(TypeData::TypeParameter(info) | TypeData::Infer(info)) => info
477 .constraint
478 .is_some_and(|constraint| is_object_like_type_impl(db, constraint)),
479 _ => false,
480 }
481}
482
483pub fn is_function_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
487 is_function_type_impl(db, type_id)
488}
489
490fn is_function_type_impl(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
491 match db.lookup(type_id) {
492 Some(TypeData::Function(_) | TypeData::Callable(_)) => true,
493 Some(TypeData::Intersection(members)) => {
494 let members = db.type_list(members);
495 members
496 .iter()
497 .any(|&member| is_function_type_impl(db, member))
498 }
499 _ => false,
500 }
501}
502
503pub fn is_valid_spread_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
516 is_valid_spread_type_impl(db, type_id, 0)
517}
518
519fn is_valid_spread_type_impl(db: &dyn TypeDatabase, type_id: TypeId, depth: u32) -> bool {
520 if depth > 20 {
521 return true;
522 }
523 match type_id {
524 TypeId::ANY | TypeId::NEVER | TypeId::ERROR => return true,
525 _ => {}
526 }
527 match db.lookup(type_id) {
528 Some(
530 TypeData::Intrinsic(
531 IntrinsicKind::String
532 | IntrinsicKind::Number
533 | IntrinsicKind::Boolean
534 | IntrinsicKind::Bigint
535 | IntrinsicKind::Symbol
536 | IntrinsicKind::Void
537 | IntrinsicKind::Null
538 | IntrinsicKind::Undefined
539 | IntrinsicKind::Unknown,
540 )
541 | TypeData::Literal(_),
542 ) => false,
543 Some(TypeData::Union(members)) => {
545 let members = db.type_list(members);
546 members
547 .iter()
548 .all(|&m| is_valid_spread_type_impl(db, m, depth + 1))
549 }
550 Some(TypeData::Intersection(members)) => {
552 let members = db.type_list(members);
553 members
554 .iter()
555 .all(|&m| is_valid_spread_type_impl(db, m, depth + 1))
556 }
557 Some(TypeData::ReadonlyType(inner)) => is_valid_spread_type_impl(db, inner, depth + 1),
558 _ => true,
561 }
562}
563
564pub fn is_empty_object_type(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
566 match db.lookup(type_id) {
567 Some(TypeData::Object(shape_id)) => {
568 let shape = db.object_shape(shape_id);
569 shape.properties.is_empty()
570 }
571 Some(TypeData::ObjectWithIndex(shape_id)) => {
572 let shape = db.object_shape(shape_id);
573 shape.properties.is_empty()
574 && shape.string_index.is_none()
575 && shape.number_index.is_none()
576 }
577 _ => false,
578 }
579}
580
581#[derive(Debug, Clone, PartialEq, Eq)]
590pub enum ConstructorTypeKind {
591 Callable,
593 Function(crate::types::FunctionShapeId),
595 Members(Vec<TypeId>),
597 Inner(TypeId),
599 Constraint(Option<TypeId>),
601 NeedsTypeEvaluation,
603 NeedsApplicationEvaluation,
605 TypeQuery(crate::types::SymbolRef),
607 NotConstructor,
609}
610
611pub fn classify_constructor_type(db: &dyn TypeDatabase, type_id: TypeId) -> ConstructorTypeKind {
651 let Some(key) = db.lookup(type_id) else {
652 return ConstructorTypeKind::NotConstructor;
653 };
654
655 match key {
656 TypeData::Callable(_) => ConstructorTypeKind::Callable,
657 TypeData::Function(shape_id) => ConstructorTypeKind::Function(shape_id),
658 TypeData::Intersection(members_id) | TypeData::Union(members_id) => {
659 let members = db.type_list(members_id);
660 ConstructorTypeKind::Members(members.to_vec())
661 }
662 TypeData::ReadonlyType(inner) | TypeData::NoInfer(inner) => {
663 ConstructorTypeKind::Inner(inner)
664 }
665 TypeData::TypeParameter(info) | TypeData::Infer(info) => {
666 ConstructorTypeKind::Constraint(info.constraint)
667 }
668 TypeData::Conditional(_)
669 | TypeData::Mapped(_)
670 | TypeData::IndexAccess(_, _)
671 | TypeData::KeyOf(_) => ConstructorTypeKind::NeedsTypeEvaluation,
672 TypeData::Application(_) => ConstructorTypeKind::NeedsApplicationEvaluation,
673 TypeData::TypeQuery(sym_ref) => ConstructorTypeKind::TypeQuery(sym_ref),
674 TypeData::Enum(_, _)
676 | TypeData::BoundParameter(_)
677 | TypeData::Intrinsic(_)
678 | TypeData::Literal(_)
679 | TypeData::Object(_)
680 | TypeData::ObjectWithIndex(_)
681 | TypeData::Array(_)
682 | TypeData::Tuple(_)
683 | TypeData::Lazy(_)
684 | TypeData::Recursive(_)
685 | TypeData::TemplateLiteral(_)
686 | TypeData::UniqueSymbol(_)
687 | TypeData::ThisType
688 | TypeData::StringIntrinsic { .. }
689 | TypeData::ModuleNamespace(_)
690 | TypeData::Error => ConstructorTypeKind::NotConstructor,
691 }
692}
693
694#[derive(Debug, Clone)]
703pub enum StaticPropertySource {
704 Properties(Vec<crate::PropertyInfo>),
706 RecurseMembers(Vec<TypeId>),
708 RecurseSingle(TypeId),
710 NeedsEvaluation,
712 NeedsApplicationEvaluation,
714 None,
716}
717
718pub fn get_static_property_source(db: &dyn TypeDatabase, type_id: TypeId) -> StaticPropertySource {
745 let Some(key) = db.lookup(type_id) else {
746 return StaticPropertySource::None;
747 };
748
749 match key {
750 TypeData::Callable(shape_id) => {
751 let shape = db.callable_shape(shape_id);
752 StaticPropertySource::Properties(shape.properties.to_vec())
753 }
754 TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
755 let shape = db.object_shape(shape_id);
756 StaticPropertySource::Properties(shape.properties.to_vec())
757 }
758 TypeData::Intersection(members_id) | TypeData::Union(members_id) => {
759 let members = db.type_list(members_id);
760 StaticPropertySource::RecurseMembers(members.to_vec())
761 }
762 TypeData::TypeParameter(info) | TypeData::Infer(info) => {
763 if let Some(constraint) = info.constraint {
764 StaticPropertySource::RecurseSingle(constraint)
765 } else {
766 StaticPropertySource::None
767 }
768 }
769 TypeData::ReadonlyType(inner) => StaticPropertySource::RecurseSingle(inner),
770 TypeData::Conditional(_)
771 | TypeData::Mapped(_)
772 | TypeData::IndexAccess(_, _)
773 | TypeData::KeyOf(_) => StaticPropertySource::NeedsEvaluation,
774 TypeData::Application(_) => StaticPropertySource::NeedsApplicationEvaluation,
775 _ => StaticPropertySource::None,
776 }
777}
778
779pub fn has_construct_signatures(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
788 match db.lookup(type_id) {
789 Some(TypeData::Callable(shape_id)) => {
790 let shape = db.callable_shape(shape_id);
791 !shape.construct_signatures.is_empty()
792 }
793 _ => false,
794 }
795}
796
797pub fn get_symbol_ref_from_type(
801 db: &dyn TypeDatabase,
802 type_id: TypeId,
803) -> Option<crate::types::SymbolRef> {
804 match db.lookup(type_id) {
805 Some(TypeData::TypeQuery(sym_ref)) => Some(sym_ref),
806 _ => None,
807 }
808}
809
810#[derive(Debug, Clone)]
816pub enum ConstructableTypeKind {
817 CallableWithConstruct,
819 CallableMaybePrototype,
821 Function,
823 SymbolRef(crate::types::SymbolRef),
825 TypeQueryRef(crate::types::SymbolRef),
827 TypeParameterWithConstraint(TypeId),
829 TypeParameterNoConstraint,
831 Intersection(Vec<TypeId>),
833 Application,
835 Object,
837 NotConstructable,
839}
840
841pub fn classify_for_constructability(
853 db: &dyn TypeDatabase,
854 type_id: TypeId,
855) -> ConstructableTypeKind {
856 let Some(key) = db.lookup(type_id) else {
857 return ConstructableTypeKind::NotConstructable;
858 };
859
860 match key {
861 TypeData::Callable(shape_id) => {
862 let shape = db.callable_shape(shape_id);
863 if shape.construct_signatures.is_empty() {
864 ConstructableTypeKind::CallableMaybePrototype
865 } else {
866 ConstructableTypeKind::CallableWithConstruct
867 }
868 }
869 TypeData::Function(_) => ConstructableTypeKind::Function,
870 TypeData::TypeQuery(sym_ref) => ConstructableTypeKind::TypeQueryRef(sym_ref),
871 TypeData::TypeParameter(info) | TypeData::Infer(info) => {
872 if let Some(constraint) = info.constraint {
873 ConstructableTypeKind::TypeParameterWithConstraint(constraint)
874 } else {
875 ConstructableTypeKind::TypeParameterNoConstraint
876 }
877 }
878 TypeData::Intersection(members_id) => {
879 let members = db.type_list(members_id);
880 ConstructableTypeKind::Intersection(members.to_vec())
881 }
882 TypeData::Application(_) => ConstructableTypeKind::Application,
883 TypeData::Object(_) | TypeData::ObjectWithIndex(_) => ConstructableTypeKind::Object,
884 _ => ConstructableTypeKind::NotConstructable,
885 }
886}
887
888pub fn construct_to_call_callable(db: &dyn TypeDatabase, type_id: TypeId) -> Option<TypeId> {
894 match db.lookup(type_id) {
895 Some(TypeData::Callable(shape_id)) => {
896 let shape = db.callable_shape(shape_id);
897 if shape.construct_signatures.is_empty() {
898 None
899 } else {
900 Some(db.callable(crate::types::CallableShape {
901 call_signatures: shape.construct_signatures.clone(),
902 construct_signatures: Vec::new(),
903 properties: Vec::new(),
904 string_index: None,
905 number_index: None,
906 symbol: None,
907 }))
908 }
909 }
910 _ => None,
911 }
912}
913
914#[derive(Debug, Clone)]
920pub enum ConstraintTypeKind {
921 TypeParameter {
923 constraint: Option<TypeId>,
924 default: Option<TypeId>,
925 },
926 Union(Vec<TypeId>),
928 Intersection(Vec<TypeId>),
930 SymbolRef(crate::types::SymbolRef),
932 Application { app_id: u32 },
934 Mapped { mapped_id: u32 },
936 KeyOf(TypeId),
938 Resolved(TypeId),
940 NoConstraint,
942}
943
944pub fn classify_for_constraint(db: &dyn TypeDatabase, type_id: TypeId) -> ConstraintTypeKind {
946 let Some(key) = db.lookup(type_id) else {
947 return ConstraintTypeKind::NoConstraint;
948 };
949 match key {
950 TypeData::TypeParameter(info) | TypeData::Infer(info) => {
951 ConstraintTypeKind::TypeParameter {
952 constraint: info.constraint,
953 default: info.default,
954 }
955 }
956 TypeData::Union(list_id) => {
957 let members = db.type_list(list_id);
958 ConstraintTypeKind::Union(members.to_vec())
959 }
960 TypeData::Intersection(list_id) => {
961 let members = db.type_list(list_id);
962 ConstraintTypeKind::Intersection(members.to_vec())
963 }
964 TypeData::Application(app_id) => ConstraintTypeKind::Application { app_id: app_id.0 },
965 TypeData::Mapped(mapped_id) => ConstraintTypeKind::Mapped {
966 mapped_id: mapped_id.0,
967 },
968 TypeData::KeyOf(operand) => ConstraintTypeKind::KeyOf(operand),
969 TypeData::Literal(_) => ConstraintTypeKind::Resolved(type_id),
970 TypeData::BoundParameter(_)
971 | TypeData::Intrinsic(_)
972 | TypeData::Object(_)
973 | TypeData::ObjectWithIndex(_)
974 | TypeData::Array(_)
975 | TypeData::Tuple(_)
976 | TypeData::Function(_)
977 | TypeData::Callable(_)
978 | TypeData::Conditional(_)
979 | TypeData::IndexAccess(_, _)
980 | TypeData::TemplateLiteral(_)
981 | TypeData::UniqueSymbol(_)
982 | TypeData::ThisType
983 | TypeData::ReadonlyType(_)
984 | TypeData::NoInfer(_)
985 | TypeData::TypeQuery(_)
986 | TypeData::StringIntrinsic { .. }
987 | TypeData::ModuleNamespace(_)
988 | TypeData::Enum(_, _)
989 | TypeData::Lazy(_)
990 | TypeData::Recursive(_)
991 | TypeData::Error => ConstraintTypeKind::NoConstraint,
992 }
993}
994
995#[derive(Debug, Clone)]
1001pub enum SignatureTypeKind {
1002 Callable(crate::types::CallableShapeId),
1004 Function(crate::types::FunctionShapeId),
1006 Union(Vec<TypeId>),
1008 Intersection(Vec<TypeId>),
1010 ReadonlyType(TypeId),
1012 TypeParameter { constraint: Option<TypeId> },
1014 NeedsEvaluation(TypeId),
1016 NoSignatures,
1018}
1019
1020pub fn classify_for_signatures(db: &dyn TypeDatabase, type_id: TypeId) -> SignatureTypeKind {
1022 if type_id == TypeId::ERROR || type_id == TypeId::NEVER {
1024 return SignatureTypeKind::NoSignatures;
1025 }
1026 if type_id == TypeId::ANY {
1027 return SignatureTypeKind::NoSignatures;
1029 }
1030
1031 let Some(key) = db.lookup(type_id) else {
1032 return SignatureTypeKind::NoSignatures;
1033 };
1034
1035 match key {
1036 TypeData::Callable(shape_id) => SignatureTypeKind::Callable(shape_id),
1038
1039 TypeData::Function(shape_id) => SignatureTypeKind::Function(shape_id),
1041
1042 TypeData::Union(members_id) => {
1044 let members = db.type_list(members_id);
1045 SignatureTypeKind::Union(members.to_vec())
1046 }
1047
1048 TypeData::Intersection(members_id) => {
1050 let members = db.type_list(members_id);
1051 SignatureTypeKind::Intersection(members.to_vec())
1052 }
1053
1054 TypeData::ReadonlyType(inner) | TypeData::NoInfer(inner) => {
1056 SignatureTypeKind::ReadonlyType(inner)
1057 }
1058
1059 TypeData::TypeParameter(info) | TypeData::Infer(info) => SignatureTypeKind::TypeParameter {
1061 constraint: info.constraint,
1062 },
1063
1064 TypeData::Conditional(_)
1066 | TypeData::Mapped(_)
1067 | TypeData::IndexAccess(_, _)
1068 | TypeData::KeyOf(_) => SignatureTypeKind::NeedsEvaluation(type_id),
1069
1070 TypeData::BoundParameter(_)
1072 | TypeData::Intrinsic(_)
1073 | TypeData::Literal(_)
1074 | TypeData::Object(_)
1075 | TypeData::ObjectWithIndex(_)
1076 | TypeData::Array(_)
1077 | TypeData::Tuple(_)
1078 | TypeData::Lazy(_)
1079 | TypeData::Recursive(_)
1080 | TypeData::Application(_)
1081 | TypeData::TemplateLiteral(_)
1082 | TypeData::UniqueSymbol(_)
1083 | TypeData::ThisType
1084 | TypeData::TypeQuery(_)
1085 | TypeData::StringIntrinsic { .. }
1086 | TypeData::ModuleNamespace(_)
1087 | TypeData::Enum(_, _)
1088 | TypeData::Error => SignatureTypeKind::NoSignatures,
1089 }
1090}
1091
1092#[derive(Debug, Clone)]
1098pub enum IterableTypeKind {
1099 Tuple(Vec<crate::types::TupleElement>),
1101 Array(TypeId),
1103 Other,
1105}
1106
1107pub fn classify_iterable_type(db: &dyn TypeDatabase, type_id: TypeId) -> IterableTypeKind {
1109 let Some(key) = db.lookup(type_id) else {
1110 return IterableTypeKind::Other;
1111 };
1112
1113 match key {
1114 TypeData::Tuple(tuple_id) => {
1115 let elements = db.tuple_list(tuple_id);
1116 IterableTypeKind::Tuple(elements.to_vec())
1117 }
1118 TypeData::Array(elem_type) => IterableTypeKind::Array(elem_type),
1119 _ => IterableTypeKind::Other,
1120 }
1121}
1122
1123#[derive(Debug, Clone)]
1133pub enum FullIterableTypeKind {
1134 Array(TypeId),
1136 Tuple(Vec<crate::types::TupleElement>),
1138 StringLiteral(tsz_common::interner::Atom),
1140 Union(Vec<TypeId>),
1142 Intersection(Vec<TypeId>),
1144 Object(crate::types::ObjectShapeId),
1146 Application { base: TypeId },
1148 TypeParameter { constraint: Option<TypeId> },
1150 Readonly(TypeId),
1152 FunctionOrCallable,
1154 ComplexType,
1156 NotIterable,
1158}
1159
1160pub fn classify_full_iterable_type(db: &dyn TypeDatabase, type_id: TypeId) -> FullIterableTypeKind {
1164 let Some(key) = db.lookup(type_id) else {
1165 return FullIterableTypeKind::NotIterable;
1166 };
1167
1168 match key {
1169 TypeData::Array(elem) => FullIterableTypeKind::Array(elem),
1170 TypeData::Tuple(tuple_id) => {
1171 let elements = db.tuple_list(tuple_id);
1172 FullIterableTypeKind::Tuple(elements.to_vec())
1173 }
1174 TypeData::Literal(crate::LiteralValue::String(s)) => FullIterableTypeKind::StringLiteral(s),
1175 TypeData::Union(members_id) => {
1176 let members = db.type_list(members_id);
1177 FullIterableTypeKind::Union(members.to_vec())
1178 }
1179 TypeData::Intersection(members_id) => {
1180 let members = db.type_list(members_id);
1181 FullIterableTypeKind::Intersection(members.to_vec())
1182 }
1183 TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
1184 FullIterableTypeKind::Object(shape_id)
1185 }
1186 TypeData::Application(app_id) => {
1187 let app = db.type_application(app_id);
1188 FullIterableTypeKind::Application { base: app.base }
1189 }
1190 TypeData::TypeParameter(info) | TypeData::Infer(info) => {
1191 FullIterableTypeKind::TypeParameter {
1192 constraint: info.constraint,
1193 }
1194 }
1195 TypeData::ReadonlyType(inner) | TypeData::NoInfer(inner) => {
1196 FullIterableTypeKind::Readonly(inner)
1197 }
1198 TypeData::Function(_) | TypeData::Callable(_) => FullIterableTypeKind::FunctionOrCallable,
1199 TypeData::IndexAccess(_, _) | TypeData::Conditional(_) | TypeData::Mapped(_) => {
1200 FullIterableTypeKind::ComplexType
1201 }
1202 TypeData::BoundParameter(_)
1204 | TypeData::Intrinsic(_)
1205 | TypeData::Literal(_)
1206 | TypeData::Lazy(_)
1207 | TypeData::Recursive(_)
1208 | TypeData::TemplateLiteral(_)
1209 | TypeData::UniqueSymbol(_)
1210 | TypeData::ThisType
1211 | TypeData::TypeQuery(_)
1212 | TypeData::KeyOf(_)
1213 | TypeData::StringIntrinsic { .. }
1214 | TypeData::ModuleNamespace(_)
1215 | TypeData::Enum(_, _)
1216 | TypeData::Error => FullIterableTypeKind::NotIterable,
1217 }
1218}
1219
1220#[derive(Debug, Clone)]
1222pub enum AsyncIterableTypeKind {
1223 Union(Vec<TypeId>),
1225 Object(crate::types::ObjectShapeId),
1227 Readonly(TypeId),
1229 NotAsyncIterable,
1231}
1232
1233pub fn classify_async_iterable_type(
1235 db: &dyn TypeDatabase,
1236 type_id: TypeId,
1237) -> AsyncIterableTypeKind {
1238 let Some(key) = db.lookup(type_id) else {
1239 return AsyncIterableTypeKind::NotAsyncIterable;
1240 };
1241
1242 match key {
1243 TypeData::Union(members_id) => {
1244 let members = db.type_list(members_id);
1245 AsyncIterableTypeKind::Union(members.to_vec())
1246 }
1247 TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
1248 AsyncIterableTypeKind::Object(shape_id)
1249 }
1250 TypeData::ReadonlyType(inner) => AsyncIterableTypeKind::Readonly(inner),
1251 _ => AsyncIterableTypeKind::NotAsyncIterable,
1252 }
1253}
1254
1255#[derive(Debug, Clone)]
1257pub enum ForOfElementKind {
1258 Array(TypeId),
1260 Tuple(Vec<crate::types::TupleElement>),
1262 Union(Vec<TypeId>),
1264 Readonly(TypeId),
1266 String,
1268 Other,
1270}
1271
1272pub fn classify_for_of_element_type(db: &dyn TypeDatabase, type_id: TypeId) -> ForOfElementKind {
1274 let Some(key) = db.lookup(type_id) else {
1275 return ForOfElementKind::Other;
1276 };
1277
1278 match key {
1279 TypeData::Array(elem) => ForOfElementKind::Array(elem),
1280 TypeData::Tuple(tuple_id) => {
1281 let elements = db.tuple_list(tuple_id);
1282 ForOfElementKind::Tuple(elements.to_vec())
1283 }
1284 TypeData::Union(members_id) => {
1285 let members = db.type_list(members_id);
1286 ForOfElementKind::Union(members.to_vec())
1287 }
1288 TypeData::ReadonlyType(inner) | TypeData::NoInfer(inner) => {
1289 ForOfElementKind::Readonly(inner)
1290 }
1291 TypeData::Literal(crate::LiteralValue::String(_)) => ForOfElementKind::String,
1293 _ => ForOfElementKind::Other,
1294 }
1295}
1296
1297#[derive(Debug, Clone)]
1312pub enum PropertyLookupKind {
1313 Object(crate::types::ObjectShapeId),
1315 ObjectWithIndex(crate::types::ObjectShapeId),
1317 Union(Vec<TypeId>),
1319 Intersection(Vec<TypeId>),
1321 Array(TypeId),
1323 Tuple(Vec<crate::types::TupleElement>),
1325 NoProperties,
1327}
1328
1329pub fn classify_for_property_lookup(db: &dyn TypeDatabase, type_id: TypeId) -> PropertyLookupKind {
1371 let Some(key) = db.lookup(type_id) else {
1372 return PropertyLookupKind::NoProperties;
1373 };
1374
1375 match key {
1376 TypeData::Object(shape_id) => PropertyLookupKind::Object(shape_id),
1377 TypeData::ObjectWithIndex(shape_id) => PropertyLookupKind::ObjectWithIndex(shape_id),
1378 TypeData::Union(list_id) => {
1379 let members = db.type_list(list_id);
1380 PropertyLookupKind::Union(members.to_vec())
1381 }
1382 TypeData::Intersection(list_id) => {
1383 let members = db.type_list(list_id);
1384 PropertyLookupKind::Intersection(members.to_vec())
1385 }
1386 TypeData::Array(elem_type) => PropertyLookupKind::Array(elem_type),
1387 TypeData::Tuple(tuple_id) => {
1388 let elements = db.tuple_list(tuple_id);
1389 PropertyLookupKind::Tuple(elements.to_vec())
1390 }
1391 TypeData::BoundParameter(_)
1393 | TypeData::Intrinsic(_)
1394 | TypeData::Literal(_)
1395 | TypeData::Function(_)
1396 | TypeData::Callable(_)
1397 | TypeData::TypeParameter(_)
1398 | TypeData::Infer(_)
1399 | TypeData::Lazy(_)
1400 | TypeData::Recursive(_)
1401 | TypeData::Application(_)
1402 | TypeData::Conditional(_)
1403 | TypeData::Mapped(_)
1404 | TypeData::IndexAccess(_, _)
1405 | TypeData::KeyOf(_)
1406 | TypeData::TemplateLiteral(_)
1407 | TypeData::UniqueSymbol(_)
1408 | TypeData::ThisType
1409 | TypeData::TypeQuery(_)
1410 | TypeData::ReadonlyType(_)
1411 | TypeData::NoInfer(_)
1412 | TypeData::StringIntrinsic { .. }
1413 | TypeData::ModuleNamespace(_)
1414 | TypeData::Enum(_, _)
1415 | TypeData::Error => PropertyLookupKind::NoProperties,
1416 }
1417}
1418
1419#[derive(Debug, Clone)]
1425pub enum EvaluationNeeded {
1426 Resolved(TypeId),
1428 SymbolRef(crate::types::SymbolRef),
1430 TypeQuery(crate::types::SymbolRef),
1432 Application {
1434 app_id: crate::types::TypeApplicationId,
1435 },
1436 IndexAccess { object: TypeId, index: TypeId },
1438 KeyOf(TypeId),
1440 Mapped {
1442 mapped_id: crate::types::MappedTypeId,
1443 },
1444 Conditional {
1446 cond_id: crate::types::ConditionalTypeId,
1447 },
1448 Callable(crate::types::CallableShapeId),
1450 Function(crate::types::FunctionShapeId),
1452 Union(Vec<TypeId>),
1454 Intersection(Vec<TypeId>),
1456 TypeParameter { constraint: Option<TypeId> },
1458 Readonly(TypeId),
1460}
1461
1462pub fn classify_for_evaluation(db: &dyn TypeDatabase, type_id: TypeId) -> EvaluationNeeded {
1464 let Some(key) = db.lookup(type_id) else {
1465 return EvaluationNeeded::Resolved(type_id);
1466 };
1467
1468 match key {
1469 TypeData::TypeQuery(sym_ref) => EvaluationNeeded::TypeQuery(sym_ref),
1470 TypeData::Application(app_id) => EvaluationNeeded::Application { app_id },
1471 TypeData::IndexAccess(object, index) => EvaluationNeeded::IndexAccess { object, index },
1472 TypeData::KeyOf(inner) => EvaluationNeeded::KeyOf(inner),
1473 TypeData::Mapped(mapped_id) => EvaluationNeeded::Mapped { mapped_id },
1474 TypeData::Conditional(cond_id) => EvaluationNeeded::Conditional { cond_id },
1475 TypeData::Callable(shape_id) => EvaluationNeeded::Callable(shape_id),
1476 TypeData::Function(shape_id) => EvaluationNeeded::Function(shape_id),
1477 TypeData::Union(list_id) => {
1478 let members = db.type_list(list_id);
1479 EvaluationNeeded::Union(members.to_vec())
1480 }
1481 TypeData::Intersection(list_id) => {
1482 let members = db.type_list(list_id);
1483 EvaluationNeeded::Intersection(members.to_vec())
1484 }
1485 TypeData::TypeParameter(info) | TypeData::Infer(info) => EvaluationNeeded::TypeParameter {
1486 constraint: info.constraint,
1487 },
1488 TypeData::ReadonlyType(inner) | TypeData::NoInfer(inner) => {
1489 EvaluationNeeded::Readonly(inner)
1490 }
1491 TypeData::BoundParameter(_)
1493 | TypeData::Intrinsic(_)
1494 | TypeData::Literal(_)
1495 | TypeData::Object(_)
1496 | TypeData::ObjectWithIndex(_)
1497 | TypeData::Array(_)
1498 | TypeData::Tuple(_)
1499 | TypeData::Lazy(_)
1500 | TypeData::Recursive(_)
1501 | TypeData::TemplateLiteral(_)
1502 | TypeData::UniqueSymbol(_)
1503 | TypeData::ThisType
1504 | TypeData::StringIntrinsic { .. }
1505 | TypeData::ModuleNamespace(_)
1506 | TypeData::Enum(_, _)
1507 | TypeData::Error => EvaluationNeeded::Resolved(type_id),
1508 }
1509}
1510
1511pub fn evaluate_contextual_structure_with(
1516 db: &dyn QueryDatabase,
1517 type_id: TypeId,
1518 evaluate_leaf: &mut dyn FnMut(TypeId) -> TypeId,
1519) -> TypeId {
1520 fn visit(
1521 db: &dyn QueryDatabase,
1522 type_id: TypeId,
1523 evaluate_leaf: &mut dyn FnMut(TypeId) -> TypeId,
1524 ) -> TypeId {
1525 match classify_for_evaluation(db, type_id) {
1526 EvaluationNeeded::Union(members) => {
1527 let mut changed = false;
1528 let evaluated: Vec<TypeId> = members
1529 .iter()
1530 .map(|&member| {
1531 let ev = visit(db, member, evaluate_leaf);
1532 if ev != member {
1533 changed = true;
1534 }
1535 ev
1536 })
1537 .collect();
1538 if changed {
1539 db.factory().union(evaluated)
1540 } else {
1541 type_id
1542 }
1543 }
1544 EvaluationNeeded::Intersection(members) => {
1545 let mut changed = false;
1546 let evaluated: Vec<TypeId> = members
1547 .iter()
1548 .map(|&member| {
1549 let ev = visit(db, member, evaluate_leaf);
1550 if ev != member {
1551 changed = true;
1552 }
1553 ev
1554 })
1555 .collect();
1556 if changed {
1557 db.factory().intersection(evaluated)
1558 } else {
1559 type_id
1560 }
1561 }
1562 EvaluationNeeded::Application { .. }
1563 | EvaluationNeeded::Mapped { .. }
1564 | EvaluationNeeded::Conditional { .. } => {
1565 let evaluated = evaluate_leaf(type_id);
1566 if evaluated != type_id {
1567 evaluated
1568 } else {
1569 type_id
1570 }
1571 }
1572 _ if get_lazy_def_id(db, type_id).is_some() => {
1573 let evaluated = evaluate_leaf(type_id);
1574 if evaluated != type_id {
1575 evaluated
1576 } else {
1577 type_id
1578 }
1579 }
1580 _ => type_id,
1581 }
1582 }
1583
1584 visit(db, type_id, evaluate_leaf)
1585}
1586
1587#[derive(Debug, Clone)]
1593pub enum PropertyAccessClassification {
1594 Direct(TypeId),
1596 SymbolRef(crate::types::SymbolRef),
1598 TypeQuery(crate::types::SymbolRef),
1600 Application {
1602 app_id: crate::types::TypeApplicationId,
1603 },
1604 Union(Vec<TypeId>),
1606 Intersection(Vec<TypeId>),
1608 IndexAccess { object: TypeId, index: TypeId },
1610 Readonly(TypeId),
1612 Callable(TypeId),
1614 TypeParameter { constraint: Option<TypeId> },
1616 NeedsEvaluation(TypeId),
1618 Resolved(TypeId),
1620}
1621
1622pub fn classify_for_property_access(
1624 db: &dyn TypeDatabase,
1625 type_id: TypeId,
1626) -> PropertyAccessClassification {
1627 let Some(key) = db.lookup(type_id) else {
1628 return PropertyAccessClassification::Resolved(type_id);
1629 };
1630
1631 match key {
1632 TypeData::Object(_) | TypeData::ObjectWithIndex(_) => {
1633 PropertyAccessClassification::Direct(type_id)
1634 }
1635 TypeData::TypeQuery(sym_ref) => PropertyAccessClassification::TypeQuery(sym_ref),
1636 TypeData::Application(app_id) => PropertyAccessClassification::Application { app_id },
1637 TypeData::Union(list_id) => {
1638 let members = db.type_list(list_id);
1639 PropertyAccessClassification::Union(members.to_vec())
1640 }
1641 TypeData::Intersection(list_id) => {
1642 let members = db.type_list(list_id);
1643 PropertyAccessClassification::Intersection(members.to_vec())
1644 }
1645 TypeData::IndexAccess(object, index) => {
1646 PropertyAccessClassification::IndexAccess { object, index }
1647 }
1648 TypeData::ReadonlyType(inner) | TypeData::NoInfer(inner) => PropertyAccessClassification::Readonly(inner),
1649 TypeData::Function(_) | TypeData::Callable(_) => {
1650 PropertyAccessClassification::Callable(type_id)
1651 }
1652 TypeData::TypeParameter(info) | TypeData::Infer(info) => {
1653 PropertyAccessClassification::TypeParameter {
1654 constraint: info.constraint,
1655 }
1656 }
1657 TypeData::Conditional(_) | TypeData::Mapped(_) | TypeData::KeyOf(_) => {
1658 PropertyAccessClassification::NeedsEvaluation(type_id)
1659 }
1660 TypeData::BoundParameter(_)
1662 | TypeData::Intrinsic(_)
1664 | TypeData::Literal(_)
1665 | TypeData::Array(_)
1666 | TypeData::Tuple(_)
1667 | TypeData::Lazy(_)
1668 | TypeData::Recursive(_)
1669 | TypeData::TemplateLiteral(_)
1670 | TypeData::UniqueSymbol(_)
1671 | TypeData::ThisType
1672 | TypeData::StringIntrinsic { .. }
1673 | TypeData::ModuleNamespace(_)
1674 | TypeData::Error
1675 | TypeData::Enum(_, _) => PropertyAccessClassification::Resolved(type_id),
1676 }
1677}
1678
1679#[derive(Debug, Clone)]
1689pub enum TypeTraversalKind {
1690 Application {
1692 app_id: crate::types::TypeApplicationId,
1693 base: TypeId,
1694 args: Vec<TypeId>,
1695 },
1696 SymbolRef(crate::types::SymbolRef),
1698 Lazy(crate::def::DefId),
1700 TypeQuery(crate::types::SymbolRef),
1702 TypeParameter {
1704 constraint: Option<TypeId>,
1705 default: Option<TypeId>,
1706 },
1707 Members(Vec<TypeId>),
1709 Function(crate::types::FunctionShapeId),
1711 Callable(crate::types::CallableShapeId),
1713 Object(crate::types::ObjectShapeId),
1715 Array(TypeId),
1717 Tuple(crate::types::TupleListId),
1719 Conditional(crate::types::ConditionalTypeId),
1721 Mapped(crate::types::MappedTypeId),
1723 Readonly(TypeId),
1725 IndexAccess { object: TypeId, index: TypeId },
1727 KeyOf(TypeId),
1729 TemplateLiteral(Vec<TypeId>),
1731 StringIntrinsic(TypeId),
1733 Terminal,
1735}
1736
1737pub fn classify_for_traversal(db: &dyn TypeDatabase, type_id: TypeId) -> TypeTraversalKind {
1742 let Some(key) = db.lookup(type_id) else {
1743 return TypeTraversalKind::Terminal;
1744 };
1745
1746 match key {
1747 TypeData::Application(app_id) => {
1748 let app = db.type_application(app_id);
1749 TypeTraversalKind::Application {
1750 app_id,
1751 base: app.base,
1752 args: app.args.clone(),
1753 }
1754 }
1755 TypeData::TypeParameter(info) | TypeData::Infer(info) => TypeTraversalKind::TypeParameter {
1756 constraint: info.constraint,
1757 default: info.default,
1758 },
1759 TypeData::Union(list_id) | TypeData::Intersection(list_id) => {
1760 let members = db.type_list(list_id);
1761 TypeTraversalKind::Members(members.to_vec())
1762 }
1763 TypeData::Function(shape_id) => TypeTraversalKind::Function(shape_id),
1764 TypeData::Callable(shape_id) => TypeTraversalKind::Callable(shape_id),
1765 TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
1766 TypeTraversalKind::Object(shape_id)
1767 }
1768 TypeData::Array(elem) => TypeTraversalKind::Array(elem),
1769 TypeData::Tuple(list_id) => TypeTraversalKind::Tuple(list_id),
1770 TypeData::Conditional(cond_id) => TypeTraversalKind::Conditional(cond_id),
1771 TypeData::Mapped(mapped_id) => TypeTraversalKind::Mapped(mapped_id),
1772 TypeData::ReadonlyType(inner) | TypeData::NoInfer(inner) => {
1773 TypeTraversalKind::Readonly(inner)
1774 }
1775 TypeData::IndexAccess(object, index) => TypeTraversalKind::IndexAccess { object, index },
1776 TypeData::KeyOf(inner) => TypeTraversalKind::KeyOf(inner),
1777 TypeData::TemplateLiteral(list_id) => {
1779 let spans = db.template_list(list_id);
1780 let types: Vec<TypeId> = spans
1781 .iter()
1782 .filter_map(|span| match span {
1783 crate::types::TemplateSpan::Type(id) => Some(*id),
1784 _ => None,
1785 })
1786 .collect();
1787 if types.is_empty() {
1788 TypeTraversalKind::Terminal
1789 } else {
1790 TypeTraversalKind::TemplateLiteral(types)
1791 }
1792 }
1793 TypeData::StringIntrinsic { type_arg, .. } => TypeTraversalKind::StringIntrinsic(type_arg),
1795 TypeData::Lazy(def_id) => TypeTraversalKind::Lazy(def_id),
1797 TypeData::TypeQuery(symbol_ref) => TypeTraversalKind::TypeQuery(symbol_ref),
1799 TypeData::BoundParameter(_)
1801 | TypeData::Intrinsic(_)
1802 | TypeData::Literal(_)
1803 | TypeData::Recursive(_)
1804 | TypeData::UniqueSymbol(_)
1805 | TypeData::ThisType
1806 | TypeData::ModuleNamespace(_)
1807 | TypeData::Error
1808 | TypeData::Enum(_, _) => TypeTraversalKind::Terminal,
1809 }
1810}
1811
1812pub fn get_lazy_if_def(db: &dyn TypeDatabase, type_id: TypeId) -> Option<crate::def::DefId> {
1816 match db.lookup(type_id) {
1817 Some(TypeData::Lazy(def_id)) => Some(def_id),
1818 _ => None,
1819 }
1820}
1821
1822#[derive(Debug, Clone)]
1827pub enum PropertyTraversalKind {
1828 Object(std::sync::Arc<crate::types::ObjectShape>),
1829 Callable(std::sync::Arc<crate::types::CallableShape>),
1830 Members(Vec<TypeId>),
1831 Other,
1832}
1833
1834pub fn classify_property_traversal(
1836 db: &dyn TypeDatabase,
1837 type_id: TypeId,
1838) -> PropertyTraversalKind {
1839 match classify_for_traversal(db, type_id) {
1840 TypeTraversalKind::Object(_) => get_object_shape(db, type_id)
1841 .map_or(PropertyTraversalKind::Other, PropertyTraversalKind::Object),
1842 TypeTraversalKind::Callable(_) => get_callable_shape(db, type_id).map_or(
1843 PropertyTraversalKind::Other,
1844 PropertyTraversalKind::Callable,
1845 ),
1846 TypeTraversalKind::Members(members) => PropertyTraversalKind::Members(members),
1847 _ => PropertyTraversalKind::Other,
1848 }
1849}
1850
1851pub fn collect_property_name_atoms_for_diagnostics(
1855 db: &dyn TypeDatabase,
1856 type_id: TypeId,
1857 max_depth: usize,
1858) -> Vec<Atom> {
1859 fn collect_inner(
1860 db: &dyn TypeDatabase,
1861 type_id: TypeId,
1862 out: &mut Vec<Atom>,
1863 depth: usize,
1864 max_depth: usize,
1865 ) {
1866 if depth > max_depth {
1867 return;
1868 }
1869 match classify_property_traversal(db, type_id) {
1870 PropertyTraversalKind::Object(shape) => {
1871 for prop in &shape.properties {
1872 out.push(prop.name);
1873 }
1874 }
1875 PropertyTraversalKind::Callable(shape) => {
1876 for prop in &shape.properties {
1877 out.push(prop.name);
1878 }
1879 }
1880 PropertyTraversalKind::Members(members) => {
1881 for member in members {
1882 collect_inner(db, member, out, depth + 1, max_depth);
1883 }
1884 }
1885 PropertyTraversalKind::Other => {}
1886 }
1887 }
1888
1889 let mut atoms = Vec::new();
1890 collect_inner(db, type_id, &mut atoms, 0, max_depth);
1891 atoms.sort_unstable();
1892 atoms.dedup();
1893 atoms
1894}
1895
1896pub fn collect_accessible_property_names_for_suggestion(
1901 db: &dyn TypeDatabase,
1902 type_id: TypeId,
1903 max_depth: usize,
1904) -> Vec<Atom> {
1905 if let Some(TypeData::Union(list_id)) = db.lookup(type_id) {
1906 let members = db.type_list(list_id).to_vec();
1907 if members.is_empty() {
1908 return vec![];
1909 }
1910 let mut common = collect_property_name_atoms_for_diagnostics(db, members[0], max_depth);
1911 common.sort_unstable();
1912 common.dedup();
1913 for &member in &members[1..] {
1914 let mut member_props =
1915 collect_property_name_atoms_for_diagnostics(db, member, max_depth);
1916 member_props.sort_unstable();
1917 member_props.dedup();
1918 common.retain(|a| member_props.binary_search(a).is_ok());
1919 if common.is_empty() {
1920 return vec![];
1921 }
1922 }
1923 return common;
1924 }
1925 collect_property_name_atoms_for_diagnostics(db, type_id, max_depth)
1926}
1927
1928pub fn is_only_null_or_undefined(db: &dyn TypeDatabase, type_id: TypeId) -> bool {
1930 if type_id == TypeId::NULL || type_id == TypeId::UNDEFINED {
1931 return true;
1932 }
1933 match db.lookup(type_id) {
1934 Some(TypeData::Intrinsic(IntrinsicKind::Null | IntrinsicKind::Undefined)) => true,
1935 Some(TypeData::Union(list_id)) => {
1936 let members = db.type_list(list_id);
1937 members.iter().all(|&m| is_only_null_or_undefined(db, m))
1938 }
1939 _ => false,
1940 }
1941}