Skip to main content

tsz_solver/
contextual.rs

1//! Contextual typing (reverse inference).
2//!
3//! Contextual typing allows type information to flow "backwards" from
4//! an expected type to an expression. This is used for:
5//! - Arrow function parameters: `const f: (x: string) => void = (x) => ...`
6//! - Array literals: `const arr: number[] = [1, 2, 3]`
7//! - Object literals: `const obj: {x: number} = {x: 1}`
8//!
9//! The key insight is that when we have an expected type, we can use it
10//! to infer types for parts of the expression that would otherwise be unknown.
11
12use crate::TypeDatabase;
13#[cfg(test)]
14use crate::types::*;
15use crate::types::{
16    CallableShapeId, FunctionShapeId, IntrinsicKind, LiteralValue, ObjectShapeId, ParamInfo,
17    TupleListId, TypeApplicationId, TypeData, TypeId, TypeListId,
18};
19use crate::visitor::TypeVisitor;
20
21// =============================================================================
22// Helper Functions
23// =============================================================================
24
25/// Helper to collect types and return None, single type, or union.
26///
27/// This pattern appears frequently in visitor implementations:
28/// - If no types collected: return None
29/// - If one type collected: return Some(that type)
30/// - If multiple types: return Some(union of types)
31fn collect_single_or_union(db: &dyn TypeDatabase, types: Vec<TypeId>) -> Option<TypeId> {
32    match types.len() {
33        0 => None,
34        1 => Some(types[0]),
35        _ => Some(db.union(types)),
36    }
37}
38
39// =============================================================================
40// Visitor Pattern Implementations
41// =============================================================================
42
43/// Visitor to extract the `this` type from callable types.
44struct ThisTypeExtractor<'a> {
45    db: &'a dyn TypeDatabase,
46}
47
48impl<'a> ThisTypeExtractor<'a> {
49    fn new(db: &'a dyn TypeDatabase) -> Self {
50        Self { db }
51    }
52
53    fn extract(&mut self, type_id: TypeId) -> Option<TypeId> {
54        self.visit_type(self.db, type_id)
55    }
56}
57
58impl<'a> TypeVisitor for ThisTypeExtractor<'a> {
59    type Output = Option<TypeId>;
60
61    fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
62        None
63    }
64
65    fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
66        None
67    }
68
69    fn visit_function(&mut self, shape_id: u32) -> Self::Output {
70        self.db.function_shape(FunctionShapeId(shape_id)).this_type
71    }
72
73    fn visit_callable(&mut self, shape_id: u32) -> Self::Output {
74        let shape = self.db.callable_shape(CallableShapeId(shape_id));
75        // Collect this types from all signatures
76        let this_types: Vec<TypeId> = shape
77            .call_signatures
78            .iter()
79            .filter_map(|sig| sig.this_type)
80            .collect();
81
82        collect_single_or_union(self.db, this_types)
83    }
84
85    fn default_output() -> Self::Output {
86        None
87    }
88}
89
90/// Visitor to extract the return type from callable types.
91struct ReturnTypeExtractor<'a> {
92    db: &'a dyn TypeDatabase,
93}
94
95impl<'a> ReturnTypeExtractor<'a> {
96    fn new(db: &'a dyn TypeDatabase) -> Self {
97        Self { db }
98    }
99
100    fn extract(&mut self, type_id: TypeId) -> Option<TypeId> {
101        self.visit_type(self.db, type_id)
102    }
103}
104
105impl<'a> TypeVisitor for ReturnTypeExtractor<'a> {
106    type Output = Option<TypeId>;
107
108    fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
109        None
110    }
111
112    fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
113        None
114    }
115
116    fn visit_function(&mut self, shape_id: u32) -> Self::Output {
117        Some(
118            self.db
119                .function_shape(FunctionShapeId(shape_id))
120                .return_type,
121        )
122    }
123
124    fn visit_callable(&mut self, shape_id: u32) -> Self::Output {
125        let shape = self.db.callable_shape(CallableShapeId(shape_id));
126        // Collect return types from all signatures
127        let return_types: Vec<TypeId> = shape
128            .call_signatures
129            .iter()
130            .map(|sig| sig.return_type)
131            .collect();
132
133        collect_single_or_union(self.db, return_types)
134    }
135
136    fn visit_union(&mut self, list_id: u32) -> Self::Output {
137        // For unions of callable types, extract return type from each member
138        // and create a union of the results.
139        let members = self.db.type_list(TypeListId(list_id));
140        let types: Vec<TypeId> = members
141            .iter()
142            .filter_map(|&member| {
143                let mut extractor = ReturnTypeExtractor::new(self.db);
144                extractor.extract(member)
145            })
146            .collect();
147        collect_single_or_union(self.db, types)
148    }
149
150    fn default_output() -> Self::Output {
151        None
152    }
153}
154
155/// Visitor to extract the type T from `ThisType`<T> utility type markers.
156///
157/// This handles the Vue 2 / Options API pattern where contextual types contain
158/// `ThisType`<T> markers to override the type of 'this' in object literal methods.
159///
160/// Example:
161/// ```typescript
162/// type ObjectDescriptor<D, M> = {
163///     methods?: M & ThisType<D & M>;
164/// };
165/// ```
166struct ThisTypeMarkerExtractor<'a> {
167    db: &'a dyn TypeDatabase,
168}
169
170impl<'a> ThisTypeMarkerExtractor<'a> {
171    fn new(db: &'a dyn TypeDatabase) -> Self {
172        Self { db }
173    }
174
175    fn extract(&mut self, type_id: TypeId) -> Option<TypeId> {
176        self.visit_type(self.db, type_id)
177    }
178
179    /// Check if a type application is for the `ThisType` utility.
180    fn is_this_type_application(&self, app_id: u32) -> bool {
181        let app = self.db.type_application(TypeApplicationId(app_id));
182
183        // CRITICAL: We must NOT return true for all Lazy types!
184        // Doing so would break ALL generic type aliases (Partial<T>, Readonly<T>, etc.)
185        // We must check if the base type is specifically "ThisType"
186
187        // Check TypeParameter case first (easier - has name directly)
188        if let Some(TypeData::TypeParameter(tp)) = self.db.lookup(app.base) {
189            let name = self.db.resolve_atom_ref(tp.name);
190            return name.as_ref() == "ThisType";
191        }
192
193        // For Lazy types (type aliases), we need to resolve the def_id to a name
194        // This is harder without access to the symbol table. For now, we fail safe
195        // and return false rather than breaking all type aliases.
196        // TODO: When we have access to symbol resolution, check if def_id points to lib.d.ts ThisType
197        if let Some(TypeData::Lazy(_def_id)) = self.db.lookup(app.base) {
198            // Cannot safely identify ThisType without symbol table access
199            // Return false to avoid breaking other type aliases
200            return false;
201        }
202
203        false
204    }
205}
206
207impl<'a> TypeVisitor for ThisTypeMarkerExtractor<'a> {
208    type Output = Option<TypeId>;
209
210    fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
211        None
212    }
213
214    fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
215        None
216    }
217
218    fn visit_application(&mut self, app_id: u32) -> Self::Output {
219        if self.is_this_type_application(app_id) {
220            let app = self.db.type_application(TypeApplicationId(app_id));
221            // ThisType<T> has exactly one type argument T
222            app.args.first().copied()
223        } else {
224            // Not a ThisType application, recurse into base and args
225            let app = self.db.type_application(TypeApplicationId(app_id));
226            let base_result = self.visit_type(self.db, app.base);
227
228            // Collect results from all arguments
229            let arg_results: Vec<_> = app
230                .args
231                .iter()
232                .filter_map(|&arg_id| self.visit_type(self.db, arg_id))
233                .collect();
234
235            // If we found ThisType in arguments, return the first one
236            // (ThisType should only appear once in a given type structure)
237            if let Some(result) = base_result {
238                Some(result)
239            } else if let Some(&first) = arg_results.first() {
240                Some(first)
241            } else {
242                None
243            }
244        }
245    }
246
247    fn visit_intersection(&mut self, list_id: u32) -> Self::Output {
248        let members = self.db.type_list(TypeListId(list_id));
249
250        // Collect all ThisType markers from the intersection
251        let this_types: Vec<TypeId> = members
252            .iter()
253            .filter_map(|&member_id| self.visit_type(self.db, member_id))
254            .collect();
255
256        if this_types.is_empty() {
257            None
258        } else if this_types.len() == 1 {
259            Some(this_types[0])
260        } else {
261            // Multiple ThisType markers - intersect them
262            // ThisType<A> & ThisType<B> => this is A & B
263            Some(self.db.intersection(this_types))
264        }
265    }
266
267    fn visit_union(&mut self, list_id: u32) -> Self::Output {
268        // For unions, we distribute over members
269        // (A & ThisType<X>) | (B & ThisType<Y>) should try each member
270        let members = self.db.type_list(TypeListId(list_id));
271
272        // TODO: This blindly picks the first ThisType.
273        // Correct behavior requires narrowing the contextual type based on
274        // the object literal shape BEFORE determining which this type to use.
275        // Example: If context is (A & ThisType<X>) | (B & ThisType<Y>) and
276        // the literal is { type: 'b' }, we should pick ThisType<Y>, not ThisType<X>.
277        // This is a conservative heuristic and could be improved.
278        members
279            .iter()
280            .find_map(|&member_id| self.visit_type(self.db, member_id))
281    }
282
283    fn default_output() -> Self::Output {
284        None
285    }
286}
287
288/// Visitor to extract array element type or union of tuple element types.
289struct ArrayElementExtractor<'a> {
290    db: &'a dyn TypeDatabase,
291}
292
293impl<'a> ArrayElementExtractor<'a> {
294    fn new(db: &'a dyn TypeDatabase) -> Self {
295        Self { db }
296    }
297
298    fn extract(&mut self, type_id: TypeId) -> Option<TypeId> {
299        self.visit_type(self.db, type_id)
300    }
301}
302
303impl<'a> TypeVisitor for ArrayElementExtractor<'a> {
304    type Output = Option<TypeId>;
305
306    fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
307        None
308    }
309
310    fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
311        None
312    }
313
314    fn visit_array(&mut self, elem_type: TypeId) -> Self::Output {
315        Some(elem_type)
316    }
317
318    fn visit_object(&mut self, shape_id: u32) -> Self::Output {
319        let shape = self.db.object_shape(ObjectShapeId(shape_id));
320        if let Some(ref idx) = shape.number_index {
321            return Some(idx.value_type);
322        }
323        if let Some(ref idx) = shape.string_index {
324            return Some(idx.value_type);
325        }
326        None
327    }
328
329    fn visit_object_with_index(&mut self, shape_id: u32) -> Self::Output {
330        self.visit_object(shape_id)
331    }
332
333    fn visit_tuple(&mut self, elements_id: u32) -> Self::Output {
334        let elements = self.db.tuple_list(TupleListId(elements_id));
335        if elements.is_empty() {
336            None
337        } else {
338            let types: Vec<TypeId> = elements.iter().map(|e| e.type_id).collect();
339            Some(self.db.union(types))
340        }
341    }
342
343    fn default_output() -> Self::Output {
344        None
345    }
346}
347
348/// Visitor to extract tuple element at a specific index.
349struct TupleElementExtractor<'a> {
350    db: &'a dyn TypeDatabase,
351    index: usize,
352}
353
354impl<'a> TupleElementExtractor<'a> {
355    fn new(db: &'a dyn TypeDatabase, index: usize) -> Self {
356        Self { db, index }
357    }
358
359    fn extract(&mut self, type_id: TypeId) -> Option<TypeId> {
360        self.visit_type(self.db, type_id)
361    }
362}
363
364impl<'a> TypeVisitor for TupleElementExtractor<'a> {
365    type Output = Option<TypeId>;
366
367    fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
368        None
369    }
370
371    fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
372        None
373    }
374
375    fn visit_tuple(&mut self, elements_id: u32) -> Self::Output {
376        let elements = self.db.tuple_list(TupleListId(elements_id));
377        if self.index < elements.len() {
378            Some(elements[self.index].type_id)
379        } else if let Some(last) = elements.last() {
380            last.rest.then_some(last.type_id)
381        } else {
382            None
383        }
384    }
385
386    fn visit_array(&mut self, elem_type: TypeId) -> Self::Output {
387        Some(elem_type)
388    }
389
390    fn visit_object(&mut self, shape_id: u32) -> Self::Output {
391        let shape = self.db.object_shape(ObjectShapeId(shape_id));
392        if let Some(ref idx) = shape.number_index {
393            return Some(idx.value_type);
394        }
395        if let Some(ref idx) = shape.string_index {
396            return Some(idx.value_type);
397        }
398        None
399    }
400
401    fn visit_object_with_index(&mut self, shape_id: u32) -> Self::Output {
402        self.visit_object(shape_id)
403    }
404
405    fn visit_union(&mut self, list_id: u32) -> Self::Output {
406        // For unions of tuple/array types, extract the element type from each member
407        // and create a union of the results.
408        let members = self.db.type_list(TypeListId(list_id));
409        let types: Vec<TypeId> = members
410            .iter()
411            .filter_map(|&member| {
412                let mut extractor = TupleElementExtractor::new(self.db, self.index);
413                extractor.extract(member)
414            })
415            .collect();
416        collect_single_or_union(self.db, types)
417    }
418
419    fn visit_intersection(&mut self, list_id: u32) -> Self::Output {
420        // For intersections, try each member and return the first match.
421        let members = self.db.type_list(TypeListId(list_id));
422        for &member in members.iter() {
423            let mut extractor = TupleElementExtractor::new(self.db, self.index);
424            if let Some(ty) = extractor.extract(member) {
425                return Some(ty);
426            }
427        }
428        None
429    }
430
431    fn default_output() -> Self::Output {
432        None
433    }
434}
435
436/// Visitor to extract property type from object types by name.
437struct PropertyExtractor<'a> {
438    db: &'a dyn TypeDatabase,
439    name: String,
440}
441
442impl<'a> PropertyExtractor<'a> {
443    fn new(db: &'a dyn TypeDatabase, name: String) -> Self {
444        Self { db, name }
445    }
446
447    fn extract(&mut self, type_id: TypeId) -> Option<TypeId> {
448        self.visit_type(self.db, type_id)
449    }
450}
451
452impl<'a> TypeVisitor for PropertyExtractor<'a> {
453    type Output = Option<TypeId>;
454
455    fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
456        None
457    }
458
459    fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
460        None
461    }
462
463    fn visit_object(&mut self, shape_id: u32) -> Self::Output {
464        let shape = self.db.object_shape(ObjectShapeId(shape_id));
465        for prop in &shape.properties {
466            if self.db.resolve_atom_ref(prop.name).as_ref() == self.name {
467                return Some(prop.type_id);
468            }
469        }
470        // Fall back to index signatures for Object types too
471        // This handles cases where interfaces/types have index signatures
472        // but are stored as Object rather than ObjectWithIndex
473        // For numeric property names (e.g., "1"), check number index signature first
474        if self.name.parse::<f64>().is_ok()
475            && let Some(ref idx) = shape.number_index
476        {
477            return Some(idx.value_type);
478        }
479        if let Some(ref idx) = shape.string_index {
480            return Some(idx.value_type);
481        }
482        None
483    }
484
485    fn visit_object_with_index(&mut self, shape_id: u32) -> Self::Output {
486        // First try named properties
487        if let Some(ty) = self.visit_object(shape_id) {
488            return Some(ty);
489        }
490        let shape = self.db.object_shape(ObjectShapeId(shape_id));
491        // For numeric property names, check number index signature first
492        if self.name.parse::<f64>().is_ok()
493            && let Some(ref idx) = shape.number_index
494        {
495            return Some(idx.value_type);
496        }
497        // Fall back to string index signature value type
498        if let Some(ref idx) = shape.string_index {
499            return Some(idx.value_type);
500        }
501        None
502    }
503
504    fn visit_lazy(&mut self, def_id: u32) -> Self::Output {
505        let resolved = crate::evaluate::evaluate_type(self.db, TypeId(def_id));
506        if resolved != TypeId(def_id) {
507            self.visit_type(self.db, resolved)
508        } else {
509            None
510        }
511    }
512
513    fn visit_union(&mut self, list_id: u32) -> Self::Output {
514        // For unions, extract the property from each member and combine as a union.
515        // e.g., for { foo: ... } with contextual type A | B,
516        // the contextual type of `foo` is A["foo"] | B["foo"].
517        let members = self.db.type_list(TypeListId(list_id));
518        let types: Vec<TypeId> = members
519            .iter()
520            .filter_map(|&member| {
521                let mut extractor = PropertyExtractor::new(self.db, self.name.clone());
522                extractor.extract(member)
523            })
524            .collect();
525        collect_single_or_union(self.db, types)
526    }
527
528    fn visit_intersection(&mut self, list_id: u32) -> Self::Output {
529        let members = self.db.type_list(TypeListId(list_id));
530        for &member in members.iter() {
531            let mut extractor = PropertyExtractor::new(self.db, self.name.clone());
532            if let Some(ty) = extractor.extract(member) {
533                return Some(ty);
534            }
535        }
536        None
537    }
538
539    fn default_output() -> Self::Output {
540        None
541    }
542}
543
544/// Extract the parameter type at `index` from a parameter list, handling rest params.
545fn extract_param_type_at(
546    db: &dyn TypeDatabase,
547    params: &[ParamInfo],
548    index: usize,
549) -> Option<TypeId> {
550    let rest_param = params.last().filter(|p| p.rest);
551    let rest_start = if rest_param.is_some() {
552        params.len().saturating_sub(1)
553    } else {
554        params.len()
555    };
556
557    // Regular (non-rest) parameters: return directly by index
558    if index < rest_start {
559        return Some(params[index].type_id);
560    }
561
562    // Rest parameter handling
563    if let Some(last_param) = rest_param {
564        // Adjust index relative to rest parameter start
565        let rest_index = index - rest_start;
566        if let Some(TypeData::Array(elem)) = db.lookup(last_param.type_id) {
567            return Some(elem);
568        }
569        if let Some(TypeData::Tuple(elements)) = db.lookup(last_param.type_id) {
570            let elements = db.tuple_list(elements);
571            if rest_index < elements.len() {
572                return Some(elements[rest_index].type_id);
573            } else if let Some(last_elem) = elements.last()
574                && last_elem.rest
575            {
576                return Some(last_elem.type_id);
577            }
578            // If out of bounds of the tuple constraint without rest, return undefined/unknown?
579            // Fall through
580        } else if let Some(TypeData::TypeParameter(param_info)) = db.lookup(last_param.type_id) {
581            if let Some(constraint) = param_info.constraint {
582                let mut mock_params = params.to_vec();
583                mock_params.last_mut().unwrap().type_id = constraint;
584                return extract_param_type_at(db, &mock_params, index);
585            }
586        } else if let Some(TypeData::Intersection(members)) = db.lookup(last_param.type_id) {
587            let members = db.type_list(members);
588            for &m in members.iter() {
589                let mut mock_params = params.to_vec();
590                mock_params.last_mut().unwrap().type_id = m;
591                if let Some(param_type) = extract_param_type_at(db, &mock_params, index) {
592                    // Try to evaluate it if it's a generic type or placeholder to see if it yields a concrete type
593                    if !matches!(
594                        db.lookup(param_type),
595                        Some(TypeData::TypeParameter(_) | TypeData::Intersection(_))
596                    ) {
597                        return Some(param_type);
598                    }
599                }
600            }
601            // If all returned generic types, just fall through
602        } else if let Some(TypeData::Application(_app_id)) = db.lookup(last_param.type_id) {
603            let evaluated = crate::evaluate::evaluate_type(db, last_param.type_id);
604            if evaluated != last_param.type_id {
605                let mut mock_params = params.to_vec();
606                mock_params.last_mut().unwrap().type_id = evaluated;
607                return extract_param_type_at(db, &mock_params, index);
608            }
609        }
610
611        // If we still didn't extract a specific type, check constraint
612        if let Some(constraint) =
613            crate::type_queries::get_type_parameter_constraint(db, last_param.type_id)
614        {
615            let mut mock_params = params.to_vec();
616            mock_params.last_mut().unwrap().type_id = constraint;
617            if let Some(param_type) = extract_param_type_at(db, &mock_params, index) {
618                // If it yielded something different than the constraint itself, use it
619                if param_type != constraint {
620                    return Some(param_type);
621                }
622            }
623        }
624
625        return Some(last_param.type_id);
626    }
627
628    // Index within non-rest params
629    (index < params.len()).then(|| params[index].type_id)
630}
631
632/// Visitor to extract parameter type from callable types.
633struct ParameterExtractor<'a> {
634    db: &'a dyn TypeDatabase,
635    index: usize,
636    no_implicit_any: bool,
637}
638
639impl<'a> ParameterExtractor<'a> {
640    fn new(db: &'a dyn TypeDatabase, index: usize, no_implicit_any: bool) -> Self {
641        Self {
642            db,
643            index,
644            no_implicit_any,
645        }
646    }
647
648    fn extract(&mut self, type_id: TypeId) -> Option<TypeId> {
649        self.visit_type(self.db, type_id)
650    }
651}
652
653impl<'a> TypeVisitor for ParameterExtractor<'a> {
654    type Output = Option<TypeId>;
655
656    fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
657        None
658    }
659
660    fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
661        None
662    }
663
664    fn visit_function(&mut self, shape_id: u32) -> Self::Output {
665        let shape = self.db.function_shape(FunctionShapeId(shape_id));
666        extract_param_type_at(self.db, &shape.params, self.index)
667    }
668
669    fn visit_callable(&mut self, shape_id: u32) -> Self::Output {
670        let shape = self.db.callable_shape(CallableShapeId(shape_id));
671        // Collect parameter types from all signatures at the given index
672        let param_types: Vec<TypeId> = shape
673            .call_signatures
674            .iter()
675            .filter_map(|sig| extract_param_type_at(self.db, &sig.params, self.index))
676            .collect();
677
678        if param_types.is_empty() {
679            None
680        } else if param_types.len() == 1 {
681            Some(param_types[0])
682        } else {
683            // Multiple different parameter types
684            // If noImplicitAny is false, fall back to `any` (return None)
685            // If noImplicitAny is true, create a union type
686            self.no_implicit_any.then(|| self.db.union(param_types))
687        }
688    }
689
690    fn visit_union(&mut self, list_id: u32) -> Self::Output {
691        // For unions of callable types, extract the parameter type from each member
692        // and create a union of the results.
693        // e.g., ((a: number) => void) | ((a: string) => void) at index 0 => number | string
694        let members = self.db.type_list(TypeListId(list_id));
695        let types: Vec<TypeId> = members
696            .iter()
697            .filter_map(|&member| {
698                let mut extractor =
699                    ParameterExtractor::new(self.db, self.index, self.no_implicit_any);
700                extractor.extract(member)
701            })
702            .collect();
703        collect_single_or_union(self.db, types)
704    }
705
706    fn visit_intersection(&mut self, list_id: u32) -> Self::Output {
707        // For intersections, try each member and return the first match.
708        let members = self.db.type_list(TypeListId(list_id));
709        for &member in members.iter() {
710            let mut extractor = ParameterExtractor::new(self.db, self.index, self.no_implicit_any);
711            if let Some(ty) = extractor.extract(member) {
712                return Some(ty);
713            }
714        }
715        None
716    }
717
718    fn default_output() -> Self::Output {
719        None
720    }
721}
722
723/// Visitor to extract parameter type from callable types for a call site.
724/// Filters signatures by arity (`arg_count`) to handle overloaded functions.
725struct ParameterForCallExtractor<'a> {
726    db: &'a dyn TypeDatabase,
727    index: usize,
728    arg_count: usize,
729}
730
731impl<'a> ParameterForCallExtractor<'a> {
732    fn new(db: &'a dyn TypeDatabase, index: usize, arg_count: usize) -> Self {
733        Self {
734            db,
735            index,
736            arg_count,
737        }
738    }
739
740    fn extract(&mut self, type_id: TypeId) -> Option<TypeId> {
741        self.visit_type(self.db, type_id)
742    }
743
744    fn signature_accepts_arg_count(&self, params: &[ParamInfo], arg_count: usize) -> bool {
745        // Count required (non-optional) parameters
746        let required_count = params.iter().filter(|p| !p.optional).count();
747
748        // Check if there's a rest parameter
749        let has_rest = params.iter().any(|p| p.rest);
750
751        if has_rest {
752            // With rest parameter: arity must be >= required_count
753            arg_count >= required_count
754        } else {
755            // Without rest parameter: arity must be within [required_count, total_count]
756            arg_count >= required_count && arg_count <= params.len()
757        }
758    }
759}
760
761impl<'a> TypeVisitor for ParameterForCallExtractor<'a> {
762    type Output = Option<TypeId>;
763
764    fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
765        None
766    }
767
768    fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
769        None
770    }
771
772    fn visit_function(&mut self, shape_id: u32) -> Self::Output {
773        let shape = self.db.function_shape(FunctionShapeId(shape_id));
774
775        if !self.signature_accepts_arg_count(&shape.params, self.arg_count) {
776            return None;
777        }
778
779        extract_param_type_at(self.db, &shape.params, self.index)
780    }
781
782    fn visit_callable(&mut self, shape_id: u32) -> Self::Output {
783        let shape = self.db.callable_shape(CallableShapeId(shape_id));
784
785        let mut matched = false;
786        let mut param_types: Vec<TypeId> = Vec::new();
787
788        for sig in &shape.call_signatures {
789            if self.signature_accepts_arg_count(&sig.params, self.arg_count) {
790                matched = true;
791                if let Some(param_type) = extract_param_type_at(self.db, &sig.params, self.index) {
792                    param_types.push(param_type);
793                }
794            }
795        }
796
797        if param_types.is_empty() && !matched {
798            param_types = shape
799                .call_signatures
800                .iter()
801                .filter_map(|sig| extract_param_type_at(self.db, &sig.params, self.index))
802                .collect();
803        }
804
805        // If no call signatures matched, check non-generic construct signatures.
806        // This handles super() calls and new expressions where the callee
807        // is a Callable with construct signatures (not call signatures).
808        // Skip generic construct signatures: their type parameters must be
809        // inferred by the solver, not used as contextual types for arguments.
810        if param_types.is_empty() {
811            matched = false;
812            for sig in &shape.construct_signatures {
813                if !sig.type_params.is_empty() {
814                    continue;
815                }
816                if self.signature_accepts_arg_count(&sig.params, self.arg_count) {
817                    matched = true;
818                    if let Some(param_type) =
819                        extract_param_type_at(self.db, &sig.params, self.index)
820                    {
821                        param_types.push(param_type);
822                    }
823                }
824            }
825            if param_types.is_empty() && !matched {
826                param_types = shape
827                    .construct_signatures
828                    .iter()
829                    .filter(|sig| sig.type_params.is_empty())
830                    .filter_map(|sig| extract_param_type_at(self.db, &sig.params, self.index))
831                    .collect();
832            }
833        }
834
835        collect_single_or_union(self.db, param_types)
836    }
837
838    fn visit_union(&mut self, list_id: u32) -> Self::Output {
839        // For unions, extract parameter types from each member and combine.
840        let members = self.db.type_list(TypeListId(list_id));
841        let types: Vec<TypeId> = members
842            .iter()
843            .filter_map(|&member| {
844                let mut extractor =
845                    ParameterForCallExtractor::new(self.db, self.index, self.arg_count);
846                extractor.extract(member)
847            })
848            .collect();
849        collect_single_or_union(self.db, types)
850    }
851
852    fn default_output() -> Self::Output {
853        None
854    }
855}
856
857/// Visitor to extract a type argument at a given index from an Application type.
858///
859/// Used for `Generator<Y, R, N>` and similar generic types where we need to
860/// pull out a specific type parameter by position.
861struct ApplicationArgExtractor<'a> {
862    db: &'a dyn TypeDatabase,
863    arg_index: usize,
864}
865
866impl<'a> ApplicationArgExtractor<'a> {
867    fn new(db: &'a dyn TypeDatabase, arg_index: usize) -> Self {
868        Self { db, arg_index }
869    }
870
871    fn extract(&mut self, type_id: TypeId) -> Option<TypeId> {
872        self.visit_type(self.db, type_id)
873    }
874}
875
876impl<'a> TypeVisitor for ApplicationArgExtractor<'a> {
877    type Output = Option<TypeId>;
878
879    fn visit_intrinsic(&mut self, _kind: IntrinsicKind) -> Self::Output {
880        None
881    }
882
883    fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
884        None
885    }
886
887    fn visit_application(&mut self, app_id: u32) -> Self::Output {
888        let app = self.db.type_application(TypeApplicationId(app_id));
889        app.args.get(self.arg_index).copied()
890    }
891
892    fn default_output() -> Self::Output {
893        None
894    }
895}
896
897/// Context for contextual typing.
898/// Holds the expected type and provides methods to extract type information.
899pub struct ContextualTypeContext<'a> {
900    interner: &'a dyn TypeDatabase,
901    /// The expected type (contextual type)
902    expected: Option<TypeId>,
903    /// Whether noImplicitAny is enabled (affects contextual typing for multi-signature functions)
904    no_implicit_any: bool,
905}
906
907impl<'a> ContextualTypeContext<'a> {
908    /// Create a new contextual type context.
909    /// Defaults to `no_implicit_any: false` for compatibility.
910    pub fn new(interner: &'a dyn TypeDatabase) -> Self {
911        ContextualTypeContext {
912            interner,
913            expected: None,
914            no_implicit_any: false,
915        }
916    }
917
918    /// Create a context with an expected type.
919    /// Defaults to `no_implicit_any: false` for compatibility.
920    pub fn with_expected(interner: &'a dyn TypeDatabase, expected: TypeId) -> Self {
921        ContextualTypeContext {
922            interner,
923            expected: Some(expected),
924            no_implicit_any: false,
925        }
926    }
927
928    /// Create a context with an expected type and explicit noImplicitAny setting.
929    pub fn with_expected_and_options(
930        interner: &'a dyn TypeDatabase,
931        expected: TypeId,
932        no_implicit_any: bool,
933    ) -> Self {
934        ContextualTypeContext {
935            interner,
936            expected: Some(expected),
937            no_implicit_any,
938        }
939    }
940
941    /// Get the expected type.
942    pub const fn expected(&self) -> Option<TypeId> {
943        self.expected
944    }
945
946    /// Check if we have a contextual type.
947    pub const fn has_context(&self) -> bool {
948        self.expected.is_some()
949    }
950
951    /// Get the contextual type for a function parameter at the given index.
952    ///
953    /// Example:
954    /// ```typescript
955    /// type Handler = (e: string, i: number) => void;
956    /// const h: Handler = (x, y) => {};  // x: string, y: number from context
957    /// ```
958    pub fn get_parameter_type(&self, index: usize) -> Option<TypeId> {
959        let expected = self.expected?;
960
961        // Handle Union explicitly - collect parameter types from all members
962        if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
963            let members = self.interner.type_list(members);
964            let param_types: Vec<TypeId> = members
965                .iter()
966                .filter_map(|&m| {
967                    let ctx = ContextualTypeContext::with_expected_and_options(
968                        self.interner,
969                        m,
970                        self.no_implicit_any,
971                    );
972                    ctx.get_parameter_type(index)
973                })
974                .collect();
975
976            return collect_single_or_union(self.interner, param_types);
977        }
978
979        // Handle Application explicitly - unwrap to base type
980        if let Some(TypeData::Application(app_id)) = self.interner.lookup(expected) {
981            let app = self.interner.type_application(app_id);
982            let ctx = ContextualTypeContext::with_expected_and_options(
983                self.interner,
984                app.base,
985                self.no_implicit_any,
986            );
987            return ctx.get_parameter_type(index);
988        }
989
990        // Handle Intersection explicitly - pick the first callable member's parameter type
991        if let Some(TypeData::Intersection(members)) = self.interner.lookup(expected) {
992            let members = self.interner.type_list(members);
993            for &m in members.iter() {
994                let ctx = ContextualTypeContext::with_expected_and_options(
995                    self.interner,
996                    m,
997                    self.no_implicit_any,
998                );
999                if let Some(param_type) = ctx.get_parameter_type(index) {
1000                    return Some(param_type);
1001                }
1002            }
1003            return None;
1004        }
1005
1006        // Handle Mapped, Conditional, and Lazy types by evaluating them first
1007        if let Some(TypeData::Mapped(_) | TypeData::Conditional(_) | TypeData::Lazy(_)) =
1008            self.interner.lookup(expected)
1009        {
1010            let evaluated = crate::evaluate::evaluate_type(self.interner, expected);
1011            if evaluated != expected {
1012                let ctx = ContextualTypeContext::with_expected_and_options(
1013                    self.interner,
1014                    evaluated,
1015                    self.no_implicit_any,
1016                );
1017                return ctx.get_parameter_type(index);
1018            }
1019        }
1020
1021        // Use visitor for Function/Callable types
1022        let mut extractor = ParameterExtractor::new(self.interner, index, self.no_implicit_any);
1023        extractor.extract(expected)
1024    }
1025
1026    /// Get the contextual type for a call argument at the given index and arity.
1027    pub fn get_parameter_type_for_call(&self, index: usize, arg_count: usize) -> Option<TypeId> {
1028        let expected = self.expected?;
1029
1030        // Handle Union explicitly - collect parameter types from all members
1031        if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
1032            let members = self.interner.type_list(members);
1033            let param_types: Vec<TypeId> = members
1034                .iter()
1035                .filter_map(|&m| {
1036                    let ctx = ContextualTypeContext::with_expected(self.interner, m);
1037                    ctx.get_parameter_type_for_call(index, arg_count)
1038                })
1039                .collect();
1040
1041            return collect_single_or_union(self.interner, param_types);
1042        }
1043
1044        // Handle Application explicitly - unwrap to base type
1045        if let Some(TypeData::Application(app_id)) = self.interner.lookup(expected) {
1046            let app = self.interner.type_application(app_id);
1047            let ctx = ContextualTypeContext::with_expected(self.interner, app.base);
1048            return ctx.get_parameter_type_for_call(index, arg_count);
1049        }
1050
1051        // Handle Intersection explicitly - pick the first callable member's parameter type
1052        if let Some(TypeData::Intersection(members)) = self.interner.lookup(expected) {
1053            let members = self.interner.type_list(members);
1054            for &m in members.iter() {
1055                let ctx = ContextualTypeContext::with_expected(self.interner, m);
1056                if let Some(param_type) = ctx.get_parameter_type_for_call(index, arg_count) {
1057                    return Some(param_type);
1058                }
1059            }
1060            return None;
1061        }
1062
1063        // Use visitor for Function/Callable types
1064        let mut extractor = ParameterForCallExtractor::new(self.interner, index, arg_count);
1065        extractor.extract(expected)
1066    }
1067
1068    /// Get the contextual type for a `this` parameter, if present on the expected type.
1069    pub fn get_this_type(&self) -> Option<TypeId> {
1070        let expected = self.expected?;
1071
1072        // Handle Union explicitly - collect this types from all members
1073        if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
1074            let members = self.interner.type_list(members);
1075            let this_types: Vec<TypeId> = members
1076                .iter()
1077                .filter_map(|&m| {
1078                    let ctx = ContextualTypeContext::with_expected(self.interner, m);
1079                    ctx.get_this_type()
1080                })
1081                .collect();
1082
1083            return collect_single_or_union(self.interner, this_types);
1084        }
1085
1086        // Handle Application explicitly - unwrap to base type
1087        if let Some(TypeData::Application(app_id)) = self.interner.lookup(expected) {
1088            let app = self.interner.type_application(app_id);
1089            let ctx = ContextualTypeContext::with_expected(self.interner, app.base);
1090            return ctx.get_this_type();
1091        }
1092
1093        // Use visitor for Function/Callable types
1094        let mut extractor = ThisTypeExtractor::new(self.interner);
1095        extractor.extract(expected)
1096    }
1097
1098    /// Get the type T from a `ThisType`<T> marker in the contextual type.
1099    ///
1100    /// This is used for the Vue 2 / Options API pattern where object literal
1101    /// methods have their `this` type overridden by contextual markers.
1102    ///
1103    /// Example:
1104    /// ```typescript
1105    /// type ObjectDescriptor<D, M> = {
1106    ///     methods?: M & ThisType<D & M>;
1107    /// };
1108    /// const obj: ObjectDescriptor<{x: number}, {greet(): void}> = {
1109    ///     methods: {
1110    ///         greet() { console.log(this.x); } // this is D & M
1111    ///     }
1112    /// };
1113    /// ```
1114    pub fn get_this_type_from_marker(&self) -> Option<TypeId> {
1115        let expected = self.expected?;
1116        let mut extractor = ThisTypeMarkerExtractor::new(self.interner);
1117        extractor.extract(expected)
1118    }
1119
1120    /// Get the contextual return type for a function.
1121    pub fn get_return_type(&self) -> Option<TypeId> {
1122        let expected = self.expected?;
1123
1124        // Handle Union explicitly - collect return types from all members
1125        if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
1126            let members = self.interner.type_list(members);
1127            let return_types: Vec<TypeId> = members
1128                .iter()
1129                .filter_map(|&m| {
1130                    let ctx = ContextualTypeContext::with_expected(self.interner, m);
1131                    ctx.get_return_type()
1132                })
1133                .collect();
1134
1135            return collect_single_or_union(self.interner, return_types);
1136        }
1137
1138        // Handle Application explicitly - unwrap to base type
1139        if let Some(TypeData::Application(app_id)) = self.interner.lookup(expected) {
1140            let app = self.interner.type_application(app_id);
1141            let ctx = ContextualTypeContext::with_expected(self.interner, app.base);
1142            return ctx.get_return_type();
1143        }
1144
1145        // Handle Lazy, Mapped, and Conditional types by evaluating first
1146        if let Some(TypeData::Lazy(_) | TypeData::Mapped(_) | TypeData::Conditional(_)) =
1147            self.interner.lookup(expected)
1148        {
1149            let evaluated = crate::evaluate::evaluate_type(self.interner, expected);
1150            if evaluated != expected {
1151                let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
1152                return ctx.get_return_type();
1153            }
1154        }
1155
1156        // Use visitor for Function/Callable types
1157        let mut extractor = ReturnTypeExtractor::new(self.interner);
1158        extractor.extract(expected)
1159    }
1160
1161    /// Get the contextual element type for an array.
1162    ///
1163    /// Example:
1164    /// ```typescript
1165    /// const arr: number[] = [1, 2, 3];  // elements are contextually typed as number
1166    /// ```
1167    pub fn get_array_element_type(&self) -> Option<TypeId> {
1168        let expected = self.expected?;
1169
1170        // Handle Union explicitly - collect element types from all array members
1171        if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
1172            let members = self.interner.type_list(members);
1173            let elem_types: Vec<TypeId> = members
1174                .iter()
1175                .filter_map(|&m| {
1176                    let ctx = ContextualTypeContext::with_expected(self.interner, m);
1177                    ctx.get_array_element_type()
1178                })
1179                .collect();
1180            return collect_single_or_union(self.interner, elem_types);
1181        }
1182
1183        // Handle Application explicitly - evaluate to resolve type aliases
1184        if let Some(TypeData::Application(_)) = self.interner.lookup(expected) {
1185            let evaluated = crate::evaluate::evaluate_type(self.interner, expected);
1186            if evaluated != expected {
1187                let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
1188                return ctx.get_array_element_type();
1189            }
1190        }
1191
1192        // Handle Mapped/Conditional/Lazy types
1193        if let Some(TypeData::Mapped(_) | TypeData::Conditional(_) | TypeData::Lazy(_)) =
1194            self.interner.lookup(expected)
1195        {
1196            let evaluated = crate::evaluate::evaluate_type(self.interner, expected);
1197            if evaluated != expected {
1198                let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
1199                return ctx.get_array_element_type();
1200            }
1201        }
1202
1203        // Handle TypeParameter - use its constraint for element extraction
1204        if let Some(constraint) =
1205            crate::type_queries::get_type_parameter_constraint(self.interner, expected)
1206        {
1207            let ctx = ContextualTypeContext::with_expected(self.interner, constraint);
1208            return ctx.get_array_element_type();
1209        }
1210
1211        // Handle Intersection - pick the first array member's element type
1212        if let Some(TypeData::Intersection(members)) = self.interner.lookup(expected) {
1213            let members = self.interner.type_list(members);
1214            for &m in members.iter() {
1215                let ctx = ContextualTypeContext::with_expected(self.interner, m);
1216                if let Some(elem_type) = ctx.get_array_element_type() {
1217                    return Some(elem_type);
1218                }
1219            }
1220            return None;
1221        }
1222
1223        let mut extractor = ArrayElementExtractor::new(self.interner);
1224        extractor.extract(expected)
1225    }
1226
1227    /// Get the contextual type for a specific tuple element.
1228    pub fn get_tuple_element_type(&self, index: usize) -> Option<TypeId> {
1229        let expected = self.expected?;
1230
1231        // Handle Union explicitly - collect tuple element types from all members
1232        if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
1233            let members = self.interner.type_list(members);
1234            let elem_types: Vec<TypeId> = members
1235                .iter()
1236                .filter_map(|&m| {
1237                    let ctx = ContextualTypeContext::with_expected(self.interner, m);
1238                    ctx.get_tuple_element_type(index)
1239                })
1240                .collect();
1241            return collect_single_or_union(self.interner, elem_types);
1242        }
1243
1244        // Handle Application explicitly - evaluate to resolve type aliases
1245        if let Some(TypeData::Application(_)) = self.interner.lookup(expected) {
1246            let evaluated = crate::evaluate::evaluate_type(self.interner, expected);
1247            if evaluated != expected {
1248                let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
1249                return ctx.get_tuple_element_type(index);
1250            }
1251        }
1252
1253        // Handle TypeParameter - use its constraint
1254        if let Some(constraint) =
1255            crate::type_queries::get_type_parameter_constraint(self.interner, expected)
1256        {
1257            let ctx = ContextualTypeContext::with_expected(self.interner, constraint);
1258            return ctx.get_tuple_element_type(index);
1259        }
1260
1261        // Handle Mapped, Conditional, and Lazy types by evaluating them first
1262        if let Some(TypeData::Mapped(_) | TypeData::Conditional(_) | TypeData::Lazy(_)) =
1263            self.interner.lookup(expected)
1264        {
1265            let evaluated = crate::evaluate::evaluate_type(self.interner, expected);
1266            if evaluated != expected {
1267                let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
1268                return ctx.get_tuple_element_type(index);
1269            }
1270        }
1271
1272        let mut extractor = TupleElementExtractor::new(self.interner, index);
1273        extractor.extract(expected)
1274    }
1275
1276    /// Get the contextual type for an object property.
1277    ///
1278    /// Example:
1279    /// ```typescript
1280    /// const obj: {x: number, y: string} = {x: 1, y: "hi"};
1281    /// ```
1282    pub fn get_property_type(&self, name: &str) -> Option<TypeId> {
1283        let expected = self.expected?;
1284
1285        // Handle Union explicitly - collect property types from all members
1286        if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
1287            let members = self.interner.type_list(members);
1288            let prop_types: Vec<TypeId> = members
1289                .iter()
1290                .filter_map(|&m| {
1291                    let ctx = ContextualTypeContext::with_expected(self.interner, m);
1292                    ctx.get_property_type(name)
1293                })
1294                .collect();
1295
1296            return if prop_types.is_empty() {
1297                None
1298            } else if prop_types.len() == 1 {
1299                Some(prop_types[0])
1300            } else {
1301                // CRITICAL: Use union_preserve_members to keep literal types intact
1302                // For discriminated unions like `{ success: false } | { success: true }`,
1303                // the property type should be `false | true`, NOT widened to `boolean`.
1304                // This preserves literal types for contextual typing.
1305                Some(self.interner.union_preserve_members(prop_types))
1306            };
1307        }
1308
1309        // Handle Mapped, Conditional, and Application types.
1310        // These complex types need to be resolved to concrete object types before
1311        // property extraction can work.
1312        match self.interner.lookup(expected) {
1313            Some(TypeData::Mapped(mapped_id)) => {
1314                // First try evaluating the mapped type directly
1315                let evaluated = crate::evaluate::evaluate_type(self.interner, expected);
1316                if evaluated != expected {
1317                    let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
1318                    return ctx.get_property_type(name);
1319                }
1320                // If evaluation deferred (e.g. { [K in keyof T]: TakeString } where T is a type
1321                // parameter), use the mapped type's template as the contextual property type
1322                // IF the template doesn't reference the mapped type's bound parameter.
1323                // For example, { [P in keyof T]: TakeString } has template=TakeString which
1324                // is independent of P, so it's safe to use directly. But { [P in K]: T[P] }
1325                // has template=T[P] which depends on P, so we can't use it.
1326                let mapped = self.interner.mapped_type(mapped_id);
1327                if mapped.template != TypeId::ANY
1328                    && mapped.template != TypeId::ERROR
1329                    && mapped.template != TypeId::NEVER
1330                    && !crate::visitor::contains_type_matching(
1331                        self.interner,
1332                        mapped.template,
1333                        |key| matches!(key, TypeData::BoundParameter(_)),
1334                    )
1335                {
1336                    return Some(mapped.template);
1337                }
1338                // Fall back to the constraint of the mapped type's source.
1339                // For `keyof P` where `P extends Props`, use `Props` as the contextual type.
1340                if let Some(TypeData::KeyOf(operand)) = self.interner.lookup(mapped.constraint) {
1341                    // The operand may be a Lazy type wrapping a type parameter — resolve it
1342                    let resolved_operand = crate::evaluate::evaluate_type(self.interner, operand);
1343                    if let Some(constraint) = crate::type_queries::get_type_parameter_constraint(
1344                        self.interner,
1345                        resolved_operand,
1346                    ) {
1347                        let ctx = ContextualTypeContext::with_expected(self.interner, constraint);
1348                        return ctx.get_property_type(name);
1349                    }
1350                    // Also try the original operand (may already be a TypeParameter)
1351                    if let Some(constraint) =
1352                        crate::type_queries::get_type_parameter_constraint(self.interner, operand)
1353                    {
1354                        let ctx = ContextualTypeContext::with_expected(self.interner, constraint);
1355                        return ctx.get_property_type(name);
1356                    }
1357                }
1358            }
1359            Some(TypeData::Application(app_id)) => {
1360                let evaluated = crate::evaluate::evaluate_type(self.interner, expected);
1361                if evaluated != expected {
1362                    let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
1363                    return ctx.get_property_type(name);
1364                }
1365                // Fallback for unevaluated Application types (e.g. Readonly<T>, Partial<T>).
1366                // When evaluation fails (e.g. due to RefCell borrow conflicts during contextual
1367                // typing), try to extract the property from the type argument directly.
1368                // This is correct for homomorphic mapped types where property types are preserved.
1369                let app = self.interner.type_application(app_id);
1370                if !app.args.is_empty() {
1371                    let ctx = ContextualTypeContext::with_expected(self.interner, app.args[0]);
1372                    if let Some(prop) = ctx.get_property_type(name) {
1373                        return Some(prop);
1374                    }
1375                }
1376            }
1377            Some(TypeData::Conditional(_) | TypeData::Lazy(_)) => {
1378                let evaluated = crate::evaluate::evaluate_type(self.interner, expected);
1379                if evaluated != expected {
1380                    let ctx = ContextualTypeContext::with_expected(self.interner, evaluated);
1381                    return ctx.get_property_type(name);
1382                }
1383            }
1384            _ => {}
1385        }
1386
1387        // Handle TypeParameter - use its constraint for property extraction
1388        // Example: Actions extends ActionsObject<State>
1389        // When getting property from Actions, use ActionsObject<State> instead
1390        if let Some(constraint) =
1391            crate::type_queries::get_type_parameter_constraint(self.interner, expected)
1392        {
1393            let ctx = ContextualTypeContext::with_expected(self.interner, constraint);
1394            return ctx.get_property_type(name);
1395        }
1396
1397        // Use visitor for Object types
1398        let mut extractor = PropertyExtractor::new(self.interner, name.to_string());
1399        extractor.extract(expected)
1400    }
1401
1402    /// Create a child context for a nested expression.
1403    /// This is used when checking nested structures with contextual types.
1404    pub fn for_property(&self, name: &str) -> Self {
1405        match self.get_property_type(name) {
1406            Some(ty) => ContextualTypeContext::with_expected(self.interner, ty),
1407            None => ContextualTypeContext::new(self.interner),
1408        }
1409    }
1410
1411    /// Create a child context for an array element.
1412    pub fn for_array_element(&self) -> Self {
1413        match self.get_array_element_type() {
1414            Some(ty) => ContextualTypeContext::with_expected(self.interner, ty),
1415            None => ContextualTypeContext::new(self.interner),
1416        }
1417    }
1418
1419    /// Create a child context for a tuple element at the given index.
1420    pub fn for_tuple_element(&self, index: usize) -> Self {
1421        match self.get_tuple_element_type(index) {
1422            Some(ty) => ContextualTypeContext::with_expected(self.interner, ty),
1423            None => ContextualTypeContext::new(self.interner),
1424        }
1425    }
1426
1427    /// Create a child context for a function parameter at the given index.
1428    pub fn for_parameter(&self, index: usize) -> Self {
1429        match self.get_parameter_type(index) {
1430            Some(ty) => ContextualTypeContext::with_expected(self.interner, ty),
1431            None => ContextualTypeContext::new(self.interner),
1432        }
1433    }
1434
1435    /// Create a child context for a function return expression.
1436    pub fn for_return(&self) -> Self {
1437        match self.get_return_type() {
1438            Some(ty) => ContextualTypeContext::with_expected(self.interner, ty),
1439            None => ContextualTypeContext::new(self.interner),
1440        }
1441    }
1442
1443    /// Get the contextual yield type for a generator function.
1444    ///
1445    /// If the expected type is `Generator<Y, R, N>`, this returns Y.
1446    /// This is used to contextually type yield expressions.
1447    ///
1448    /// Example:
1449    /// ```typescript
1450    /// function* gen(): Generator<number, void, unknown> {
1451    ///     yield 1;  // 1 is contextually typed as number
1452    /// }
1453    /// ```
1454    pub fn get_generator_yield_type(&self) -> Option<TypeId> {
1455        let expected = self.expected?;
1456
1457        // Handle Union explicitly - collect yield types from all members
1458        if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
1459            let members = self.interner.type_list(members);
1460            let yield_types: Vec<TypeId> = members
1461                .iter()
1462                .filter_map(|&m| {
1463                    let ctx = ContextualTypeContext::with_expected(self.interner, m);
1464                    ctx.get_generator_yield_type()
1465                })
1466                .collect();
1467
1468            return collect_single_or_union(self.interner, yield_types);
1469        }
1470
1471        // Generator<Y, R, N> — yield type is arg 0
1472        let mut extractor = ApplicationArgExtractor::new(self.interner, 0);
1473        extractor.extract(expected)
1474    }
1475
1476    /// Get the contextual return type for a generator function (`TReturn` from Generator<Y, `TReturn`, N>).
1477    ///
1478    /// This is used to contextually type return statements in generators.
1479    pub fn get_generator_return_type(&self) -> Option<TypeId> {
1480        let expected = self.expected?;
1481
1482        // Handle Union explicitly - collect return types from all members
1483        if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
1484            let members = self.interner.type_list(members);
1485            let return_types: Vec<TypeId> = members
1486                .iter()
1487                .filter_map(|&m| {
1488                    let ctx = ContextualTypeContext::with_expected(self.interner, m);
1489                    ctx.get_generator_return_type()
1490                })
1491                .collect();
1492
1493            return collect_single_or_union(self.interner, return_types);
1494        }
1495
1496        // Generator<Y, R, N> — return type is arg 1
1497        let mut extractor = ApplicationArgExtractor::new(self.interner, 1);
1498        extractor.extract(expected)
1499    }
1500
1501    /// Get the contextual next type for a generator function (`TNext` from Generator<Y, R, `TNext`>).
1502    ///
1503    /// This is used to determine the type of values passed to .`next()` and
1504    /// the type of the yield expression result.
1505    pub fn get_generator_next_type(&self) -> Option<TypeId> {
1506        let expected = self.expected?;
1507
1508        // Handle Union explicitly - collect next types from all members
1509        if let Some(TypeData::Union(members)) = self.interner.lookup(expected) {
1510            let members = self.interner.type_list(members);
1511            let next_types: Vec<TypeId> = members
1512                .iter()
1513                .filter_map(|&m| {
1514                    let ctx = ContextualTypeContext::with_expected(self.interner, m);
1515                    ctx.get_generator_next_type()
1516                })
1517                .collect();
1518
1519            return collect_single_or_union(self.interner, next_types);
1520        }
1521
1522        // Generator<Y, R, N> — next type is arg 2
1523        let mut extractor = ApplicationArgExtractor::new(self.interner, 2);
1524        extractor.extract(expected)
1525    }
1526
1527    /// Create a child context for a yield expression in a generator.
1528    pub fn for_yield(&self) -> Self {
1529        match self.get_generator_yield_type() {
1530            Some(ty) => ContextualTypeContext::with_expected(self.interner, ty),
1531            None => ContextualTypeContext::new(self.interner),
1532        }
1533    }
1534}
1535
1536/// Apply contextual type to infer a more specific type.
1537///
1538/// This implements bidirectional type inference:
1539/// 1. If `expr_type` is any/unknown/error, use contextual type
1540/// 2. If `expr_type` is a literal and contextual type is a union containing that literal's base type, preserve literal
1541/// 3. If `expr_type` is assignable to contextual type and is more specific, use `expr_type`
1542/// 4. Otherwise, prefer `expr_type` (don't widen to contextual type)
1543pub fn apply_contextual_type(
1544    interner: &dyn TypeDatabase,
1545    expr_type: TypeId,
1546    contextual_type: Option<TypeId>,
1547) -> TypeId {
1548    let ctx_type = match contextual_type {
1549        Some(t) => t,
1550        None => return expr_type,
1551    };
1552
1553    // If expression type is any, unknown, or error, use contextual type
1554    if expr_type.is_any_or_unknown() || expr_type.is_error() {
1555        return ctx_type;
1556    }
1557
1558    // If expression type is the same, just return it
1559    if expr_type == ctx_type {
1560        return expr_type;
1561    }
1562
1563    // Check if expr_type is a literal type that should be preserved
1564    // When contextual type is a union like string | number, we should preserve literal types
1565    if let Some(expr_key) = interner.lookup(expr_type) {
1566        // Literal types should be preserved when context is a union
1567        if matches!(expr_key, TypeData::Literal(_))
1568            && let Some(ctx_key) = interner.lookup(ctx_type)
1569            && matches!(ctx_key, TypeData::Union(_))
1570        {
1571            // Preserve the literal type - it's more specific than the union
1572            return expr_type;
1573        }
1574    }
1575
1576    // PERF: Reuse a single SubtypeChecker across all subtype checks in this function
1577    let mut checker = crate::subtype::SubtypeChecker::new(interner);
1578
1579    // Check if contextual type is a union
1580    if let Some(TypeData::Union(members)) = interner.lookup(ctx_type) {
1581        let members = interner.type_list(members);
1582        // If expr_type is in the union, it's valid - use the more specific expr_type
1583        for &member in members.iter() {
1584            if member == expr_type {
1585                return expr_type;
1586            }
1587        }
1588        // If expr_type is assignable to any union member, use expr_type
1589        for &member in members.iter() {
1590            checker.reset();
1591            if checker.is_subtype_of(expr_type, member) {
1592                return expr_type;
1593            }
1594        }
1595    }
1596
1597    // If expr_type is assignable to contextual type, use expr_type (it's more specific)
1598    checker.reset();
1599    if checker.is_subtype_of(expr_type, ctx_type) {
1600        return expr_type;
1601    }
1602
1603    // If contextual type is assignable to expr_type, use contextual type (it's more specific)
1604    // BUT: Skip for function/callable types — the solver's bivariant SubtypeChecker can
1605    // incorrectly say that a wider function type (e.g. (x: number|string) => void) is a
1606    // subtype of a narrower one (e.g. (x: number) => void), which would widen the property
1607    // type and suppress valid TS2322 errors under strict function types.
1608    let is_function_type = matches!(
1609        interner.lookup(expr_type),
1610        Some(TypeData::Function(_) | TypeData::Object(_))
1611    );
1612    if !is_function_type {
1613        checker.reset();
1614        if checker.is_subtype_of(ctx_type, expr_type) {
1615            return ctx_type;
1616        }
1617    }
1618
1619    // Default: prefer the expression type (don't widen to contextual type)
1620    // This prevents incorrectly widening concrete types to generic type parameters
1621    expr_type
1622}
1623
1624#[cfg(test)]
1625#[path = "../tests/contextual_tests.rs"]
1626mod tests;