1pub(crate) mod extractors;
16
17use crate::TypeDatabase;
18use crate::contextual::extractors::{
19 ApplicationArgExtractor, ArrayElementExtractor, ParameterExtractor, ParameterForCallExtractor,
20 PropertyExtractor, ReturnTypeExtractor, ThisTypeExtractor, ThisTypeMarkerExtractor,
21 TupleElementExtractor, collect_single_or_union,
22};
23#[cfg(test)]
24use crate::types::*;
25use crate::types::{TypeData, TypeId};
26
27pub struct ContextualTypeContext<'a> {
30 interner: &'a dyn TypeDatabase,
31 expected: Option<TypeId>,
33 no_implicit_any: bool,
35}
36
37impl<'a> ContextualTypeContext<'a> {
38 pub fn new(interner: &'a dyn TypeDatabase) -> Self {
41 ContextualTypeContext {
42 interner,
43 expected: None,
44 no_implicit_any: false,
45 }
46 }
47
48 pub fn with_expected(interner: &'a dyn TypeDatabase, expected: TypeId) -> Self {
51 ContextualTypeContext {
52 interner,
53 expected: Some(expected),
54 no_implicit_any: false,
55 }
56 }
57
58 pub fn with_expected_and_options(
60 interner: &'a dyn TypeDatabase,
61 expected: TypeId,
62 no_implicit_any: bool,
63 ) -> Self {
64 ContextualTypeContext {
65 interner,
66 expected: Some(expected),
67 no_implicit_any,
68 }
69 }
70
71 pub const fn expected(&self) -> Option<TypeId> {
73 self.expected
74 }
75
76 pub const fn has_context(&self) -> bool {
78 self.expected.is_some()
79 }
80
81 pub fn get_parameter_type(&self, index: usize) -> Option<TypeId> {
89 let expected = self.expected?;
90
91 if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
93 let members = self.interner.type_list(members);
94 let param_types: Vec<TypeId> = members
95 .iter()
96 .filter_map(|&m| {
97 let ctx = ContextualTypeContext::with_expected_and_options(
98 self.interner,
99 m,
100 self.no_implicit_any,
101 );
102 ctx.get_parameter_type(index)
103 })
104 .collect();
105
106 return collect_single_or_union(self.interner, param_types);
107 }
108
109 if let Some(TypeData::Application(app_id)) = self.interner.lookup(expected) {
111 let app = self.interner.type_application(app_id);
112 let ctx = ContextualTypeContext::with_expected_and_options(
113 self.interner,
114 app.base,
115 self.no_implicit_any,
116 );
117 return ctx.get_parameter_type(index);
118 }
119
120 if let Some(TypeData::Intersection(members)) = self.interner.lookup(expected) {
122 let members = self.interner.type_list(members);
123 for &m in members.iter() {
124 let ctx = ContextualTypeContext::with_expected_and_options(
125 self.interner,
126 m,
127 self.no_implicit_any,
128 );
129 if let Some(param_type) = ctx.get_parameter_type(index) {
130 return Some(param_type);
131 }
132 }
133 return None;
134 }
135
136 if let Some(TypeData::Mapped(_) | TypeData::Conditional(_) | TypeData::Lazy(_)) =
138 self.interner.lookup(expected)
139 {
140 let evaluated = crate::evaluation::evaluate::evaluate_type(self.interner, expected);
141 if evaluated != expected {
142 let ctx = ContextualTypeContext::with_expected_and_options(
143 self.interner,
144 evaluated,
145 self.no_implicit_any,
146 );
147 return ctx.get_parameter_type(index);
148 }
149 }
150
151 let mut extractor = ParameterExtractor::new(self.interner, index, self.no_implicit_any);
153 extractor.extract(expected)
154 }
155
156 pub fn get_parameter_type_for_call(&self, index: usize, arg_count: usize) -> Option<TypeId> {
158 let expected = self.expected?;
159
160 if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
162 let members = self.interner.type_list(members);
163 let param_types: Vec<TypeId> = members
164 .iter()
165 .filter_map(|&m| {
166 let ctx = ContextualTypeContext::with_expected(self.interner, m);
167 ctx.get_parameter_type_for_call(index, arg_count)
168 })
169 .collect();
170
171 return collect_single_or_union(self.interner, param_types);
172 }
173
174 if let Some(TypeData::Application(app_id)) = self.interner.lookup(expected) {
176 let app = self.interner.type_application(app_id);
177 let ctx = ContextualTypeContext::with_expected(self.interner, app.base);
178 return ctx.get_parameter_type_for_call(index, arg_count);
179 }
180
181 if let Some(TypeData::Intersection(members)) = self.interner.lookup(expected) {
183 let members = self.interner.type_list(members);
184 for &m in members.iter() {
185 let ctx = ContextualTypeContext::with_expected(self.interner, m);
186 if let Some(param_type) = ctx.get_parameter_type_for_call(index, arg_count) {
187 return Some(param_type);
188 }
189 }
190 return None;
191 }
192
193 let mut extractor = ParameterForCallExtractor::new(self.interner, index, arg_count);
195 extractor.extract(expected)
196 }
197
198 pub fn get_this_type(&self) -> Option<TypeId> {
200 let expected = self.expected?;
201
202 if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
204 let members = self.interner.type_list(members);
205 let this_types: Vec<TypeId> = members
206 .iter()
207 .filter_map(|&m| {
208 let ctx = ContextualTypeContext::with_expected(self.interner, m);
209 ctx.get_this_type()
210 })
211 .collect();
212
213 return collect_single_or_union(self.interner, this_types);
214 }
215
216 if let Some(TypeData::Application(app_id)) = self.interner.lookup(expected) {
218 let app = self.interner.type_application(app_id);
219 let ctx = ContextualTypeContext::with_expected(self.interner, app.base);
220 return ctx.get_this_type();
221 }
222
223 let mut extractor = ThisTypeExtractor::new(self.interner);
225 extractor.extract(expected)
226 }
227
228 pub fn get_this_type_from_marker(&self) -> Option<TypeId> {
245 let expected = self.expected?;
246 let mut extractor = ThisTypeMarkerExtractor::new(self.interner);
247 extractor.extract(expected)
248 }
249
250 pub fn get_return_type(&self) -> Option<TypeId> {
252 let expected = self.expected?;
253
254 if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
256 let members = self.interner.type_list(members);
257 let return_types: Vec<TypeId> = members
258 .iter()
259 .filter_map(|&m| {
260 let ctx = ContextualTypeContext::with_expected(self.interner, m);
261 ctx.get_return_type()
262 })
263 .collect();
264
265 return collect_single_or_union(self.interner, return_types);
266 }
267
268 if let Some(TypeData::Application(app_id)) = self.interner.lookup(expected) {
270 let app = self.interner.type_application(app_id);
271 let ctx = ContextualTypeContext::with_expected(self.interner, app.base);
272 return ctx.get_return_type();
273 }
274
275 if let Some(TypeData::Lazy(_) | TypeData::Mapped(_) | TypeData::Conditional(_)) =
277 self.interner.lookup(expected)
278 {
279 let evaluated = crate::evaluation::evaluate::evaluate_type(self.interner, expected);
280 if evaluated != expected {
281 let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
282 return ctx.get_return_type();
283 }
284 }
285
286 let mut extractor = ReturnTypeExtractor::new(self.interner);
288 extractor.extract(expected)
289 }
290
291 pub fn get_array_element_type(&self) -> Option<TypeId> {
298 let expected = self.expected?;
299
300 if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
302 let members = self.interner.type_list(members);
303 let elem_types: Vec<TypeId> = members
304 .iter()
305 .filter_map(|&m| {
306 let ctx = ContextualTypeContext::with_expected(self.interner, m);
307 ctx.get_array_element_type()
308 })
309 .collect();
310 return collect_single_or_union(self.interner, elem_types);
311 }
312
313 if let Some(TypeData::Application(app_id)) = self.interner.lookup(expected) {
318 let app = self.interner.type_application(app_id);
319 let evaluated = crate::evaluation::evaluate::evaluate_type(self.interner, expected);
321 if evaluated != expected {
322 let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
323 if let Some(elem) = ctx.get_array_element_type() {
324 return Some(elem);
325 }
326 if !app.args.is_empty() && self.is_iterable_like_object(evaluated) {
328 return Some(app.args[0]);
329 }
330 }
331 if !app.args.is_empty() && evaluated == expected {
337 return Some(app.args[0]);
338 }
339 }
340
341 if let Some(TypeData::Mapped(_) | TypeData::Conditional(_) | TypeData::Lazy(_)) =
343 self.interner.lookup(expected)
344 {
345 let evaluated = crate::evaluation::evaluate::evaluate_type(self.interner, expected);
346 if evaluated != expected {
347 let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
348 return ctx.get_array_element_type();
349 }
350 }
351
352 if let Some(constraint) =
354 crate::type_queries::get_type_parameter_constraint(self.interner, expected)
355 {
356 let ctx = ContextualTypeContext::with_expected(self.interner, constraint);
357 return ctx.get_array_element_type();
358 }
359
360 if let Some(TypeData::Intersection(members)) = self.interner.lookup(expected) {
362 let members = self.interner.type_list(members);
363 for &m in members.iter() {
364 let ctx = ContextualTypeContext::with_expected(self.interner, m);
365 if let Some(elem_type) = ctx.get_array_element_type() {
366 return Some(elem_type);
367 }
368 }
369 return None;
370 }
371
372 let mut extractor = ArrayElementExtractor::new(self.interner);
373 extractor.extract(expected)
374 }
375
376 fn is_iterable_like_object(&self, type_id: TypeId) -> bool {
380 use crate::types::TypeData;
381
382 match self.interner.lookup(type_id) {
384 Some(TypeData::Object(shape_id)) => {
385 let shape = self.interner.object_shape(shape_id);
386 if shape.number_index.is_some() {
388 return true;
389 }
390 for prop in &shape.properties {
392 let name = self.interner.resolve_atom(prop.name);
393 if name == "__@iterator" || name == "[Symbol.iterator]" {
394 return true;
395 }
396 }
397 false
398 }
399 Some(TypeData::Intersection(members)) => {
400 let members = self.interner.type_list(members);
401 members.iter().any(|&m| self.is_iterable_like_object(m))
402 }
403 _ => false,
404 }
405 }
406
407 pub fn get_tuple_element_type(&self, index: usize) -> Option<TypeId> {
409 let expected = self.expected?;
410
411 if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
413 let members = self.interner.type_list(members);
414 let elem_types: Vec<TypeId> = members
415 .iter()
416 .filter_map(|&m| {
417 let ctx = ContextualTypeContext::with_expected(self.interner, m);
418 ctx.get_tuple_element_type(index)
419 })
420 .collect();
421 return collect_single_or_union(self.interner, elem_types);
422 }
423
424 if let Some(TypeData::Application(_)) = self.interner.lookup(expected) {
426 let evaluated = crate::evaluation::evaluate::evaluate_type(self.interner, expected);
427 if evaluated != expected {
428 let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
429 return ctx.get_tuple_element_type(index);
430 }
431 }
432
433 if let Some(constraint) =
435 crate::type_queries::get_type_parameter_constraint(self.interner, expected)
436 {
437 let ctx = ContextualTypeContext::with_expected(self.interner, constraint);
438 return ctx.get_tuple_element_type(index);
439 }
440
441 if let Some(TypeData::Mapped(_) | TypeData::Conditional(_) | TypeData::Lazy(_)) =
443 self.interner.lookup(expected)
444 {
445 let evaluated = crate::evaluation::evaluate::evaluate_type(self.interner, expected);
446 if evaluated != expected {
447 let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
448 return ctx.get_tuple_element_type(index);
449 }
450 }
451
452 let mut extractor = TupleElementExtractor::new(self.interner, index);
453 extractor.extract(expected)
454 }
455
456 pub fn get_property_type(&self, name: &str) -> Option<TypeId> {
463 let expected = self.expected?;
464
465 if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
467 let members = self.interner.type_list(members);
468 let prop_types: Vec<TypeId> = members
469 .iter()
470 .filter_map(|&m| {
471 let ctx = ContextualTypeContext::with_expected(self.interner, m);
472 ctx.get_property_type(name)
473 })
474 .collect();
475
476 return if prop_types.is_empty() {
477 None
478 } else if prop_types.len() == 1 {
479 Some(prop_types[0])
480 } else {
481 Some(self.interner.union_preserve_members(prop_types))
486 };
487 }
488
489 match self.interner.lookup(expected) {
493 Some(TypeData::Mapped(mapped_id)) => {
494 let evaluated = crate::evaluation::evaluate::evaluate_type(self.interner, expected);
496 if evaluated != expected {
497 let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
498 return ctx.get_property_type(name);
499 }
500 let mapped = self.interner.mapped_type(mapped_id);
507 if mapped.template != TypeId::ANY
508 && mapped.template != TypeId::ERROR
509 && mapped.template != TypeId::NEVER
510 && !crate::visitor::contains_type_matching(
511 self.interner,
512 mapped.template,
513 |key| matches!(key, TypeData::BoundParameter(_)),
514 )
515 {
516 return Some(mapped.template);
517 }
518 if let Some(TypeData::KeyOf(operand)) = self.interner.lookup(mapped.constraint) {
521 let resolved_operand =
523 crate::evaluation::evaluate::evaluate_type(self.interner, operand);
524 if let Some(constraint) = crate::type_queries::get_type_parameter_constraint(
525 self.interner,
526 resolved_operand,
527 ) {
528 let ctx = ContextualTypeContext::with_expected(self.interner, constraint);
529 return ctx.get_property_type(name);
530 }
531 if let Some(constraint) =
533 crate::type_queries::get_type_parameter_constraint(self.interner, operand)
534 {
535 let ctx = ContextualTypeContext::with_expected(self.interner, constraint);
536 return ctx.get_property_type(name);
537 }
538 }
539 }
540 Some(TypeData::Application(app_id)) => {
541 let evaluated = crate::evaluation::evaluate::evaluate_type(self.interner, expected);
542 if evaluated != expected {
543 let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
544 return ctx.get_property_type(name);
545 }
546 let app = self.interner.type_application(app_id);
551 if !app.args.is_empty() {
552 let ctx = ContextualTypeContext::with_expected(self.interner, app.args[0]);
553 if let Some(prop) = ctx.get_property_type(name) {
554 return Some(prop);
555 }
556 }
557 }
558 Some(TypeData::Conditional(_) | TypeData::Lazy(_)) => {
559 let evaluated = crate::evaluation::evaluate::evaluate_type(self.interner, expected);
560 if evaluated != expected {
561 let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
562 return ctx.get_property_type(name);
563 }
564 }
565 _ => {}
566 }
567
568 if let Some(constraint) =
572 crate::type_queries::get_type_parameter_constraint(self.interner, expected)
573 {
574 let ctx = ContextualTypeContext::with_expected(self.interner, constraint);
575 return ctx.get_property_type(name);
576 }
577
578 let mut extractor = PropertyExtractor::new(self.interner, name);
580 extractor.extract(expected)
581 }
582
583 pub fn for_property(&self, name: &str) -> Self {
586 match self.get_property_type(name) {
587 Some(ty) => ContextualTypeContext::with_expected(self.interner, ty),
588 None => ContextualTypeContext::new(self.interner),
589 }
590 }
591
592 pub fn for_array_element(&self) -> Self {
594 match self.get_array_element_type() {
595 Some(ty) => ContextualTypeContext::with_expected(self.interner, ty),
596 None => ContextualTypeContext::new(self.interner),
597 }
598 }
599
600 pub fn for_tuple_element(&self, index: usize) -> Self {
602 match self.get_tuple_element_type(index) {
603 Some(ty) => ContextualTypeContext::with_expected(self.interner, ty),
604 None => ContextualTypeContext::new(self.interner),
605 }
606 }
607
608 pub fn for_parameter(&self, index: usize) -> Self {
610 match self.get_parameter_type(index) {
611 Some(ty) => ContextualTypeContext::with_expected(self.interner, ty),
612 None => ContextualTypeContext::new(self.interner),
613 }
614 }
615
616 pub fn for_return(&self) -> Self {
618 match self.get_return_type() {
619 Some(ty) => ContextualTypeContext::with_expected(self.interner, ty),
620 None => ContextualTypeContext::new(self.interner),
621 }
622 }
623
624 pub fn get_generator_yield_type(&self) -> Option<TypeId> {
636 let expected = self.expected?;
637
638 if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
640 let members = self.interner.type_list(members);
641 let yield_types: Vec<TypeId> = members
642 .iter()
643 .filter_map(|&m| {
644 let ctx = ContextualTypeContext::with_expected(self.interner, m);
645 ctx.get_generator_yield_type()
646 })
647 .collect();
648
649 return collect_single_or_union(self.interner, yield_types);
650 }
651
652 let mut extractor = ApplicationArgExtractor::new(self.interner, 0);
654 extractor.extract(expected)
655 }
656
657 pub fn get_generator_return_type(&self) -> Option<TypeId> {
661 let expected = self.expected?;
662
663 if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
665 let members = self.interner.type_list(members);
666 let return_types: Vec<TypeId> = members
667 .iter()
668 .filter_map(|&m| {
669 let ctx = ContextualTypeContext::with_expected(self.interner, m);
670 ctx.get_generator_return_type()
671 })
672 .collect();
673
674 return collect_single_or_union(self.interner, return_types);
675 }
676
677 let mut extractor = ApplicationArgExtractor::new(self.interner, 1);
679 extractor.extract(expected)
680 }
681
682 pub fn get_generator_next_type(&self) -> Option<TypeId> {
687 let expected = self.expected?;
688
689 if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
691 let members = self.interner.type_list(members);
692 let next_types: Vec<TypeId> = members
693 .iter()
694 .filter_map(|&m| {
695 let ctx = ContextualTypeContext::with_expected(self.interner, m);
696 ctx.get_generator_next_type()
697 })
698 .collect();
699
700 return collect_single_or_union(self.interner, next_types);
701 }
702
703 let mut extractor = ApplicationArgExtractor::new(self.interner, 2);
705 extractor.extract(expected)
706 }
707
708 pub fn for_yield(&self) -> Self {
710 match self.get_generator_yield_type() {
711 Some(ty) => ContextualTypeContext::with_expected(self.interner, ty),
712 None => ContextualTypeContext::new(self.interner),
713 }
714 }
715}
716
717pub fn apply_contextual_type(
725 interner: &dyn TypeDatabase,
726 expr_type: TypeId,
727 contextual_type: Option<TypeId>,
728) -> TypeId {
729 let ctx_type = match contextual_type {
730 Some(t) => t,
731 None => return expr_type,
732 };
733
734 if expr_type.is_any_or_unknown() || expr_type.is_error() {
736 return ctx_type;
737 }
738
739 if expr_type == ctx_type {
741 return expr_type;
742 }
743
744 if let Some(expr_key) = interner.lookup(expr_type) {
747 if matches!(expr_key, TypeData::Literal(_))
749 && let Some(ctx_key) = interner.lookup(ctx_type)
750 && matches!(ctx_key, TypeData::Union(_))
751 {
752 return expr_type;
754 }
755 }
756
757 let mut checker = crate::relations::subtype::SubtypeChecker::new(interner);
759
760 if let Some(TypeData::Union(members)) = interner.lookup(ctx_type) {
762 let members = interner.type_list(members);
763 for &member in members.iter() {
765 if member == expr_type {
766 return expr_type;
767 }
768 }
769 for &member in members.iter() {
771 checker.reset();
772 if checker.is_subtype_of(expr_type, member) {
773 return expr_type;
774 }
775 }
776 }
777
778 checker.reset();
780 if checker.is_subtype_of(expr_type, ctx_type) {
781 return expr_type;
782 }
783
784 let is_function_type = matches!(
790 interner.lookup(expr_type),
791 Some(TypeData::Function(_) | TypeData::Object(_))
792 );
793 if !is_function_type {
794 checker.reset();
795 if checker.is_subtype_of(ctx_type, expr_type) {
796 return ctx_type;
797 }
798 }
799
800 expr_type
803}
804
805#[cfg(test)]
806#[path = "../../tests/contextual_tests.rs"]
807mod tests;