1use crate::context::CheckerContext;
10use tsz_parser::parser::NodeIndex;
11use tsz_parser::parser::node::NodeAccess;
12use tsz_solver::TypeId;
13use tsz_solver::Visibility;
14use tsz_solver::recursion::{DepthCounter, RecursionProfile};
15
16pub struct TypeNodeChecker<'a, 'ctx> {
21 pub ctx: &'a mut CheckerContext<'ctx>,
22 depth: DepthCounter,
24}
25
26impl<'a, 'ctx> TypeNodeChecker<'a, 'ctx> {
27 pub const fn new(ctx: &'a mut CheckerContext<'ctx>) -> Self {
29 Self {
30 ctx,
31 depth: DepthCounter::with_profile(RecursionProfile::TypeNodeCheck),
32 }
33 }
34
35 pub fn check(&mut self, idx: NodeIndex) -> TypeId {
40 if !self.depth.enter() {
42 return TypeId::ERROR;
43 }
44
45 if let Some(&cached) = self.ctx.node_types.get(&idx.0) {
47 if cached == TypeId::ERROR {
48 self.depth.leave();
50 return cached;
51 }
52
53 if self.ctx.type_parameter_scope.is_empty() {
56 self.depth.leave();
58 return cached;
59 }
60 }
64
65 let result = self.compute_type(idx);
67 let is_type_ref = self
73 .ctx
74 .arena
75 .get(idx)
76 .is_some_and(|n| n.kind == tsz_parser::parser::syntax_kind_ext::TYPE_REFERENCE);
77 if !is_type_ref {
78 self.ctx.node_types.insert(idx.0, result);
79 }
80
81 self.depth.leave();
82 result
83 }
84
85 fn compute_type(&mut self, idx: NodeIndex) -> TypeId {
87 use tsz_parser::parser::syntax_kind_ext;
88 use tsz_scanner::SyntaxKind;
89
90 let Some(node) = self.ctx.arena.get(idx) else {
91 return TypeId::ERROR;
92 };
93
94 match node.kind {
95 k if k == SyntaxKind::NumberKeyword as u16 => TypeId::NUMBER,
97 k if k == SyntaxKind::StringKeyword as u16 => TypeId::STRING,
98 k if k == SyntaxKind::BooleanKeyword as u16 => TypeId::BOOLEAN,
99 k if k == SyntaxKind::VoidKeyword as u16 => TypeId::VOID,
100 k if k == SyntaxKind::AnyKeyword as u16 => TypeId::ANY,
101 k if k == SyntaxKind::NeverKeyword as u16 => TypeId::NEVER,
102 k if k == SyntaxKind::UnknownKeyword as u16 => TypeId::UNKNOWN,
103 k if k == SyntaxKind::UndefinedKeyword as u16 => TypeId::UNDEFINED,
104 k if k == SyntaxKind::NullKeyword as u16 => TypeId::NULL,
105 k if k == SyntaxKind::ObjectKeyword as u16 => TypeId::OBJECT,
106 k if k == SyntaxKind::BigIntKeyword as u16 => TypeId::BIGINT,
107 k if k == SyntaxKind::SymbolKeyword as u16 => TypeId::SYMBOL,
108
109 k if k == syntax_kind_ext::TYPE_REFERENCE => self.get_type_from_type_reference(idx),
111
112 k if k == syntax_kind_ext::UNION_TYPE => self.get_type_from_union_type(idx),
114
115 k if k == syntax_kind_ext::INTERSECTION_TYPE => {
117 self.get_type_from_intersection_type(idx)
118 }
119
120 k if k == syntax_kind_ext::ARRAY_TYPE => self.get_type_from_array_type(idx),
122
123 k if k == syntax_kind_ext::TUPLE_TYPE => self.get_type_from_tuple_type(idx),
125
126 k if k == syntax_kind_ext::TYPE_OPERATOR => self.get_type_from_type_operator(idx),
128
129 k if k == syntax_kind_ext::INDEXED_ACCESS_TYPE => {
131 self.get_type_from_indexed_access_type(idx)
132 }
133
134 k if k == syntax_kind_ext::FUNCTION_TYPE => self.get_type_from_function_type(idx),
136
137 k if k == syntax_kind_ext::CONSTRUCTOR_TYPE => self.get_type_from_function_type(idx),
139
140 k if k == syntax_kind_ext::TYPE_LITERAL => self.get_type_from_type_literal(idx),
142
143 k if k == syntax_kind_ext::TYPE_QUERY => self.get_type_from_type_query(idx),
145
146 k if k == syntax_kind_ext::MAPPED_TYPE => self.get_type_from_mapped_type(idx),
149
150 k if k == syntax_kind_ext::THIS_TYPE
151 || k == tsz_scanner::SyntaxKind::ThisKeyword as u16 =>
152 {
153 if !self.is_this_type_allowed(idx) {
154 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
155 self.ctx.error(
156 node.pos,
157 node.end.saturating_sub(node.pos),
158 diagnostic_messages::A_THIS_TYPE_IS_AVAILABLE_ONLY_IN_A_NON_STATIC_MEMBER_OF_A_CLASS_OR_INTERFACE.to_string(),
159 diagnostic_codes::A_THIS_TYPE_IS_AVAILABLE_ONLY_IN_A_NON_STATIC_MEMBER_OF_A_CLASS_OR_INTERFACE,
160 );
161 TypeId::ERROR
162 } else {
163 self.ctx.types.this_type()
164 }
165 }
166
167 _ => self.lower_with_resolvers(idx, true, true),
170 }
171 }
172
173 fn get_type_from_type_reference(&mut self, idx: NodeIndex) -> TypeId {
179 self.lower_with_resolvers(idx, false, false)
180 }
181
182 fn get_type_from_union_type(&mut self, idx: NodeIndex) -> TypeId {
195 let Some(node) = self.ctx.arena.get(idx) else {
196 return TypeId::ERROR;
197 };
198
199 if let Some(composite) = self.ctx.arena.get_composite_type(node) {
201 let mut member_types = Vec::new();
202 for &type_idx in &composite.types.nodes {
203 member_types.push(self.check(type_idx));
205 }
206
207 if member_types.is_empty() {
208 return TypeId::NEVER;
209 }
210
211 return tsz_solver::utils::union_or_single(self.ctx.types, member_types);
212 }
213
214 TypeId::ERROR
215 }
216
217 fn get_type_from_intersection_type(&mut self, idx: NodeIndex) -> TypeId {
226 let Some(node) = self.ctx.arena.get(idx) else {
227 return TypeId::ERROR;
228 };
229
230 if let Some(composite) = self.ctx.arena.get_composite_type(node) {
232 let mut member_types = Vec::new();
233 for &type_idx in &composite.types.nodes {
234 member_types.push(self.check(type_idx));
236 }
237
238 if member_types.is_empty() {
239 return TypeId::UNKNOWN; }
241
242 return tsz_solver::utils::intersection_or_single(self.ctx.types, member_types);
243 }
244
245 TypeId::ERROR
246 }
247
248 fn get_type_from_array_type(&mut self, idx: NodeIndex) -> TypeId {
252 let Some(node) = self.ctx.arena.get(idx) else {
253 return TypeId::ERROR;
254 };
255 let factory = self.ctx.types.factory();
256
257 if let Some(array_type) = self.ctx.arena.get_array_type(node) {
258 let elem_type = self.check(array_type.element_type);
259 return factory.array(elem_type);
260 }
261
262 TypeId::ERROR
263 }
264
265 fn get_type_from_tuple_type(&mut self, idx: NodeIndex) -> TypeId {
273 use tsz_solver::TupleElement;
274
275 let Some(node) = self.ctx.arena.get(idx) else {
276 return TypeId::ERROR;
277 };
278 let factory = self.ctx.types.factory();
279
280 if let Some(tuple_type) = self.ctx.arena.get_tuple_type(node) {
281 let mut elements = Vec::new();
282 let mut seen_optional = false;
283
284 for &elem_idx in &tuple_type.elements.nodes {
285 if elem_idx.is_none() {
286 continue;
287 }
288
289 let Some(elem_node) = self.ctx.arena.get(elem_idx) else {
290 continue;
291 };
292
293 use tsz_parser::parser::syntax_kind_ext;
295 if elem_node.kind == syntax_kind_ext::OPTIONAL_TYPE {
296 seen_optional = true;
298 if let Some(wrapped) = self.ctx.arena.get_wrapped_type(elem_node) {
299 let elem_type = self.check(wrapped.type_node);
300 elements.push(TupleElement {
301 type_id: elem_type,
302 name: None,
303 optional: true,
304 rest: false,
305 });
306 }
307 } else if elem_node.kind == syntax_kind_ext::REST_TYPE {
308 if let Some(wrapped) = self.ctx.arena.get_wrapped_type(elem_node) {
311 let elem_type = self.check(wrapped.type_node);
312 elements.push(TupleElement {
313 type_id: elem_type,
314 name: None,
315 optional: false,
316 rest: true,
317 });
318 }
319 } else {
320 if seen_optional {
323 self.ctx.error(
324 elem_node.pos,
325 elem_node.end - elem_node.pos,
326 crate::diagnostics::diagnostic_messages::A_REQUIRED_ELEMENT_CANNOT_FOLLOW_AN_OPTIONAL_ELEMENT.to_string(),
327 crate::diagnostics::diagnostic_codes::A_REQUIRED_ELEMENT_CANNOT_FOLLOW_AN_OPTIONAL_ELEMENT,
328 );
329 }
330 let elem_type = self.check(elem_idx);
331 elements.push(TupleElement {
332 type_id: elem_type,
333 name: None,
334 optional: false,
335 rest: false,
336 });
337 }
338 }
339
340 return factory.tuple(elements);
341 }
342
343 TypeId::ERROR
344 }
345
346 fn get_type_from_type_operator(&mut self, idx: NodeIndex) -> TypeId {
356 use tsz_scanner::SyntaxKind;
357 let factory = self.ctx.types.factory();
358
359 let Some(node) = self.ctx.arena.get(idx) else {
360 return TypeId::ERROR;
361 };
362
363 if let Some(type_op) = self.ctx.arena.get_type_operator(node) {
364 let operator = type_op.operator;
365 let inner_type = self.check(type_op.type_node);
366
367 if operator == SyntaxKind::ReadonlyKeyword as u16 {
369 return factory.readonly_type(inner_type);
370 }
371
372 if operator == SyntaxKind::KeyOfKeyword as u16 {
374 return factory.keyof(inner_type);
375 }
376
377 if operator == SyntaxKind::UniqueKeyword as u16 {
379 return inner_type;
382 }
383
384 inner_type
386 } else {
387 TypeId::ERROR
388 }
389 }
390
391 fn get_type_from_indexed_access_type(&mut self, idx: NodeIndex) -> TypeId {
397 let Some(node) = self.ctx.arena.get(idx) else {
398 return TypeId::ERROR;
399 };
400 let factory = self.ctx.types.factory();
401
402 if let Some(indexed_access) = self.ctx.arena.get_indexed_access_type(node) {
403 let object_type = self.check(indexed_access.object_type);
404 let index_type = self.check(indexed_access.index_type);
405
406 if let Some(invalid_member) = self.get_invalid_index_type_member(index_type)
408 && let Some(inode) = self.ctx.arena.get(indexed_access.index_type)
409 {
410 let mut formatter = self.ctx.create_type_formatter();
411 let index_type_str = formatter.format(invalid_member);
412 let message = crate::diagnostics::format_message(
413 crate::diagnostics::diagnostic_messages::TYPE_CANNOT_BE_USED_AS_AN_INDEX_TYPE,
414 &[&index_type_str],
415 );
416 self.ctx
417 .error(inode.pos, inode.end - inode.pos, message, 2538);
418 }
419
420 factory.index_access(object_type, index_type)
421 } else {
422 TypeId::ERROR
423 }
424 }
425
426 fn get_invalid_index_type_member(&self, type_id: TypeId) -> Option<TypeId> {
428 tsz_solver::type_queries::get_invalid_index_type_member(self.ctx.types, type_id)
429 }
430
431 fn get_type_from_function_type(&mut self, idx: NodeIndex) -> TypeId {
437 let Some(_node) = self.ctx.arena.get(idx) else {
438 return TypeId::ERROR;
439 };
440 let Some(func_data) = self.ctx.arena.get_function_type(_node) else {
441 return TypeId::ERROR;
442 };
443
444 check_duplicate_parameters_in_type(self.ctx, &func_data.parameters);
448 check_parameter_initializers_in_type(self.ctx, &func_data.parameters);
449
450 use tsz_parser::parser::syntax_kind_ext;
451
452 let mut local_type_params: std::collections::HashSet<String> =
454 std::collections::HashSet::new();
455 if let Some(ref type_params) = func_data.type_parameters {
456 for &tp_idx in &type_params.nodes {
457 if let Some(tp_node) = self.ctx.arena.get(tp_idx)
458 && let Some(tp_data) = self.ctx.arena.get_type_parameter(tp_node)
459 && let Some(name_node) = self.ctx.arena.get(tp_data.name)
460 && let Some(ident) = self.ctx.arena.get_identifier(name_node)
461 {
462 local_type_params.insert(ident.escaped_text.clone());
463 }
464 }
465 }
466
467 let is_builtin_type = |name: &str| -> bool {
469 matches!(
470 name,
471 "void" | "null" | "undefined" | "any" | "unknown" | "never" |
473 "number" | "bigint" | "boolean" | "string" | "symbol" | "object" |
474 "Function" | "Object" | "String" | "Number" | "Boolean" | "Symbol" |
476 "Array" | "ReadonlyArray" | "Uppercase" | "Lowercase" | "Capitalize" | "Uncapitalize"
478 )
479 };
480
481 let mut undefined_types: Vec<(NodeIndex, String)> = Vec::new();
483 let mut renamed_binding_aliases: std::collections::HashSet<String> =
484 std::collections::HashSet::new();
485
486 for ¶m_idx in &func_data.parameters.nodes {
487 let mut stack = vec![param_idx];
488 while let Some(node_idx) = stack.pop() {
489 let Some(binding_node) = self.ctx.arena.get(node_idx) else {
490 continue;
491 };
492 if binding_node.kind == syntax_kind_ext::BINDING_ELEMENT
493 && let Some(binding) = self.ctx.arena.get_binding_element(binding_node)
494 && binding.property_name.is_some()
495 && binding.name.is_some()
496 && let Some(alias_name) = self.ctx.arena.get_identifier_text(binding.name)
497 {
498 renamed_binding_aliases.insert(alias_name.to_string());
499 }
500 stack.extend(self.ctx.arena.get_children(node_idx));
501 }
502 }
503
504 let is_name_resolvable =
507 |ctx: &CheckerContext, name: &str, name_node_idx: NodeIndex| -> bool {
508 if ctx.binder.file_locals.get(name).is_some() {
510 return true;
511 }
512 if ctx
514 .lib_contexts
515 .iter()
516 .any(|lib_ctx| lib_ctx.binder.file_locals.get(name).is_some())
517 {
518 return true;
519 }
520 if ctx
522 .binder
523 .resolve_identifier(ctx.arena, name_node_idx)
524 .is_some()
525 {
526 return true;
527 }
528 false
529 };
530
531 if func_data.type_annotation.is_some()
533 && let Some(tn) = self.ctx.arena.get(func_data.type_annotation)
534 && tn.kind == syntax_kind_ext::TYPE_REFERENCE
535 && let Some(tr) = self.ctx.arena.get_type_ref(tn)
536 && let Some(name_node) = self.ctx.arena.get(tr.type_name)
537 && let Some(ident) = self.ctx.arena.get_identifier(name_node)
538 {
539 let name = &ident.escaped_text;
540 let is_builtin = is_builtin_type(name);
541 let is_local_type_param = local_type_params.contains(name);
542 let is_type_param = self.ctx.type_parameter_scope.contains_key(name);
543 let in_scope = is_name_resolvable(self.ctx, name, tr.type_name);
544
545 if !is_builtin && !is_local_type_param && !is_type_param && !in_scope {
546 undefined_types.push((tr.type_name, name.clone()));
547 }
548 }
549
550 for param_idx in &func_data.parameters.nodes {
552 if let Some(param_node) = self.ctx.arena.get(*param_idx)
553 && let Some(param_data) = self.ctx.arena.get_parameter(param_node)
554 && param_data.type_annotation.is_some()
555 && let Some(tn) = self.ctx.arena.get(param_data.type_annotation)
556 && tn.kind == syntax_kind_ext::TYPE_REFERENCE
557 && let Some(tr) = self.ctx.arena.get_type_ref(tn)
558 && let Some(name_node) = self.ctx.arena.get(tr.type_name)
559 && let Some(ident) = self.ctx.arena.get_identifier(name_node)
560 {
561 let name = &ident.escaped_text;
562 let is_builtin = is_builtin_type(name);
563 let is_local_type_param = local_type_params.contains(name);
564 let is_type_param = self.ctx.type_parameter_scope.contains_key(name);
565 let in_scope = is_name_resolvable(self.ctx, name, tr.type_name);
566
567 if !is_builtin && !is_local_type_param && !is_type_param && !in_scope {
568 undefined_types.push((tr.type_name, name.clone()));
569 }
570 }
571 }
572
573 for (error_idx, name) in undefined_types {
575 if renamed_binding_aliases.contains(&name) {
576 continue;
577 }
578 if let Some(node) = self.ctx.arena.get(error_idx) {
579 let message = format!("Cannot find name '{name}'.");
580 self.ctx.error(node.pos, node.end - node.pos, message, 2304);
581 }
582 }
583
584 self.lower_with_resolvers(idx, false, false)
586 }
587
588 fn get_type_from_type_literal(&mut self, idx: NodeIndex) -> TypeId {
590 use tsz_parser::parser::syntax_kind_ext::{
591 CALL_SIGNATURE, CONSTRUCT_SIGNATURE, METHOD_SIGNATURE, PROPERTY_SIGNATURE,
592 };
593 use tsz_solver::{
594 CallSignature, CallableShape, FunctionShape, IndexSignature, ObjectFlags, ObjectShape,
595 PropertyInfo,
596 };
597
598 let Some(node) = self.ctx.arena.get(idx) else {
599 return TypeId::ERROR;
600 };
601
602 let Some(data) = self.ctx.arena.get_type_literal(node) else {
603 return TypeId::ERROR;
604 };
605
606 let mut properties = Vec::new();
607 let mut call_signatures = Vec::new();
608 let mut construct_signatures = Vec::new();
609 let mut string_index = None;
610 let mut number_index = None;
611
612 for &member_idx in &data.members.nodes {
613 let Some(member) = self.ctx.arena.get(member_idx) else {
614 continue;
615 };
616
617 if let Some(sig) = self.ctx.arena.get_signature(member) {
618 match member.kind {
619 CALL_SIGNATURE => {
620 let (params, this_type) = self.extract_params_from_signature(sig);
621 let return_type = self
622 .resolve_return_type_with_params_in_scope(sig.type_annotation, ¶ms);
623 call_signatures.push(CallSignature {
624 type_params: Vec::new(),
625 params,
626 this_type,
627 return_type,
628 type_predicate: None,
629 is_method: false,
630 });
631 }
632 CONSTRUCT_SIGNATURE => {
633 let (params, this_type) = self.extract_params_from_signature(sig);
634 let return_type = self
635 .resolve_return_type_with_params_in_scope(sig.type_annotation, ¶ms);
636 construct_signatures.push(CallSignature {
637 type_params: Vec::new(),
638 params,
639 this_type,
640 return_type,
641 type_predicate: None,
642 is_method: false,
643 });
644 }
645 METHOD_SIGNATURE | PROPERTY_SIGNATURE => {
646 let Some(name) = self.get_property_name(sig.name) else {
647 continue;
648 };
649 let name_atom = self.ctx.types.intern_string(&name);
650
651 if member.kind == METHOD_SIGNATURE {
652 let (params, this_type) = self.extract_params_from_signature(sig);
653 let return_type = self.resolve_return_type_with_params_in_scope(
654 sig.type_annotation,
655 ¶ms,
656 );
657 let shape = FunctionShape {
658 type_params: Vec::new(),
659 params,
660 this_type,
661 return_type,
662 type_predicate: None,
663 is_constructor: false,
664 is_method: true,
665 };
666 let factory = self.ctx.types.factory();
667 let method_type = factory.function(shape);
668 properties.push(PropertyInfo {
669 name: name_atom,
670 type_id: method_type,
671 write_type: method_type,
672 optional: sig.question_token,
673 readonly: self.has_readonly_modifier(&sig.modifiers),
674 is_method: true,
675 visibility: Visibility::Public,
676 parent_id: None,
677 });
678 } else {
679 let type_id = if sig.type_annotation.is_some() {
680 self.check(sig.type_annotation)
681 } else {
682 TypeId::ANY
683 };
684 properties.push(PropertyInfo {
685 name: name_atom,
686 type_id,
687 write_type: type_id,
688 optional: sig.question_token,
689 readonly: self.has_readonly_modifier(&sig.modifiers),
690 is_method: false,
691 visibility: Visibility::Public,
692 parent_id: None,
693 });
694 }
695 }
696 _ => {}
697 }
698 continue;
699 }
700
701 if let Some(index_sig) = self.ctx.arena.get_index_signature(member) {
702 let param_idx = index_sig
703 .parameters
704 .nodes
705 .first()
706 .copied()
707 .unwrap_or(NodeIndex::NONE);
708 let Some(param_node) = self.ctx.arena.get(param_idx) else {
709 continue;
710 };
711 let Some(param_data) = self.ctx.arena.get_parameter(param_node) else {
712 continue;
713 };
714 let key_type = if param_data.type_annotation.is_some() {
715 self.check(param_data.type_annotation)
716 } else {
717 TypeId::ANY
718 };
719
720 let has_param_grammar_error =
724 param_data.dot_dot_dot_token || param_data.question_token;
725 let is_valid_index_type = key_type == TypeId::STRING
726 || key_type == TypeId::NUMBER
727 || key_type == TypeId::SYMBOL
728 || tsz_solver::visitor::is_template_literal_type(self.ctx.types, key_type);
729 if !is_valid_index_type
730 && !has_param_grammar_error
731 && let Some(pnode) = self.ctx.arena.get(param_idx)
732 {
733 self.ctx.error(
734 pnode.pos,
735 pnode.end - pnode.pos,
736 "An index signature parameter type must be 'string', 'number', 'symbol', or a template literal type.".to_string(),
737 1268,
738 );
739 }
740
741 let value_type = if index_sig.type_annotation.is_some() {
742 self.check(index_sig.type_annotation)
743 } else {
744 TypeId::ANY
745 };
746 let readonly = self.has_readonly_modifier(&index_sig.modifiers);
747 let info = IndexSignature {
748 key_type,
749 value_type,
750 readonly,
751 };
752 if key_type == TypeId::NUMBER {
753 number_index = Some(info);
754 } else {
755 string_index = Some(info);
756 }
757 continue;
758 }
759
760 if (member.kind == tsz_parser::parser::syntax_kind_ext::GET_ACCESSOR
762 || member.kind == tsz_parser::parser::syntax_kind_ext::SET_ACCESSOR)
763 && let Some(accessor) = self.ctx.arena.get_accessor(member)
764 && let Some(name) = self.get_property_name(accessor.name)
765 {
766 let name_atom = self.ctx.types.intern_string(&name);
767 let is_getter = member.kind == tsz_parser::parser::syntax_kind_ext::GET_ACCESSOR;
768 if is_getter {
769 let getter_type = if accessor.type_annotation.is_some() {
770 self.check(accessor.type_annotation)
771 } else {
772 TypeId::ANY
773 };
774 if let Some(existing) = properties.iter_mut().find(|p| p.name == name_atom) {
775 existing.type_id = getter_type;
776 } else {
777 properties.push(PropertyInfo {
778 name: name_atom,
779 type_id: getter_type,
780 write_type: getter_type,
781 optional: false,
782 readonly: false,
783 is_method: false,
784 visibility: Visibility::Public,
785 parent_id: None,
786 });
787 }
788 } else {
789 let setter_type = accessor
790 .parameters
791 .nodes
792 .first()
793 .and_then(|¶m_idx| self.ctx.arena.get(param_idx))
794 .and_then(|param_node| self.ctx.arena.get_parameter(param_node))
795 .and_then(|param| {
796 (param.type_annotation.is_some())
797 .then(|| self.check(param.type_annotation))
798 })
799 .unwrap_or(TypeId::UNKNOWN);
800 if let Some(existing) = properties.iter_mut().find(|p| p.name == name_atom) {
801 existing.write_type = setter_type;
802 existing.readonly = false;
803 } else {
804 properties.push(PropertyInfo {
805 name: name_atom,
806 type_id: setter_type,
807 write_type: setter_type,
808 optional: false,
809 readonly: false,
810 is_method: false,
811 visibility: Visibility::Public,
812 parent_id: None,
813 });
814 }
815 }
816 }
817 }
818
819 if !call_signatures.is_empty() || !construct_signatures.is_empty() {
820 let factory = self.ctx.types.factory();
821
822 return factory.callable(CallableShape {
823 call_signatures,
824 construct_signatures,
825 properties,
826 string_index,
827 number_index,
828 symbol: None,
829 });
830 }
831
832 if string_index.is_some() || number_index.is_some() {
833 let factory = self.ctx.types.factory();
834
835 return factory.object_with_index(ObjectShape {
836 flags: ObjectFlags::empty(),
837 properties,
838 string_index,
839 number_index,
840 symbol: None,
841 });
842 }
843
844 let factory = self.ctx.types.factory();
845 factory.object(properties)
846 }
847
848 fn get_type_from_type_query(&mut self, idx: NodeIndex) -> TypeId {
856 use tsz_lowering::TypeLowering;
857
858 let Some(node) = self.ctx.arena.get(idx) else {
859 return TypeId::ERROR;
860 };
861
862 let Some(type_query) = self.ctx.arena.get_type_query(node) else {
863 return TypeId::ERROR;
864 };
865
866 if let Some(&expr_type) = self.ctx.node_types.get(&type_query.expr_name.0)
869 && expr_type != TypeId::ERROR
870 {
871 return expr_type;
872 }
873
874 let name_opt = if let Some(expr_node) = self.ctx.arena.get(type_query.expr_name) {
875 if expr_node.kind == tsz_scanner::SyntaxKind::Identifier as u16 {
876 self.ctx
877 .arena
878 .get_identifier(expr_node)
879 .map(|id| id.escaped_text.as_str())
880 } else {
881 None
882 }
883 } else {
884 None
885 };
886
887 if name_opt == Some("default") {
888 use crate::diagnostics::{diagnostic_codes, diagnostic_messages, format_message};
889 let msg = format_message(diagnostic_messages::CANNOT_FIND_NAME, &["default"]);
890 self.ctx.error(
891 self.ctx.arena.get(type_query.expr_name).unwrap().pos,
892 self.ctx.arena.get(type_query.expr_name).unwrap().end
893 - self.ctx.arena.get(type_query.expr_name).unwrap().pos,
894 msg,
895 diagnostic_codes::CANNOT_FIND_NAME,
896 );
897 return TypeId::ERROR;
898 }
899
900 if let Some(expr_node) = self.ctx.arena.get(type_query.expr_name)
903 && expr_node.kind == tsz_scanner::SyntaxKind::Identifier as u16
904 && let Some(ident) = self.ctx.arena.get_identifier(expr_node)
905 && let Some(¶m_type) = self.ctx.typeof_param_scope.get(ident.escaped_text.as_str())
906 {
907 return param_type;
908 }
909
910 if let Some(sym_id) = self.resolve_type_query_symbol(type_query.expr_name) {
914 let factory = self.ctx.types.factory();
915 return factory.type_query(tsz_solver::SymbolRef(sym_id.0));
916 }
917
918 let value_resolver = |node_idx: NodeIndex| -> Option<u32> {
920 let ident = self.ctx.arena.get_identifier_at(node_idx)?;
921 let name = ident.escaped_text.as_str();
922 if name == "default" {
923 return None;
924 }
925 let sym_id = self.ctx.binder.file_locals.get(name)?;
926 Some(sym_id.0)
927 };
928 let type_resolver = |_node_idx: NodeIndex| -> Option<u32> { None };
929 let lowering = TypeLowering::with_resolvers(
930 self.ctx.arena,
931 self.ctx.types,
932 &type_resolver,
933 &value_resolver,
934 );
935
936 lowering.lower_type(idx)
937 }
938
939 fn resolve_type_query_symbol(&self, expr_name: NodeIndex) -> Option<tsz_binder::SymbolId> {
944 use tsz_parser::parser::syntax_kind_ext;
945
946 let node = self.ctx.arena.get(expr_name)?;
947
948 if node.kind == tsz_scanner::SyntaxKind::Identifier as u16 {
949 let ident = self.ctx.arena.get_identifier(node)?;
950 let name = ident.escaped_text.as_str();
951 if name == "default" {
952 return None;
953 }
954 let sym_id = self.ctx.binder.file_locals.get(name)?;
955 return Some(sym_id);
956 }
957
958 if node.kind == syntax_kind_ext::QUALIFIED_NAME {
959 let qn = self.ctx.arena.get_qualified_name(node)?;
960 let left_sym = self.resolve_type_query_symbol(qn.left)?;
962
963 let right_node = self.ctx.arena.get(qn.right)?;
965 let right_ident = self.ctx.arena.get_identifier(right_node)?;
966 let right_name = right_ident.escaped_text.as_str();
967
968 let lib_binders: Vec<std::sync::Arc<tsz_binder::BinderState>> = self
970 .ctx
971 .lib_contexts
972 .iter()
973 .map(|lc| std::sync::Arc::clone(&lc.binder))
974 .collect();
975 let left_symbol = self
976 .ctx
977 .binder
978 .get_symbol_with_libs(left_sym, &lib_binders)?;
979
980 if let Some(exports) = left_symbol.exports.as_ref()
981 && let Some(member_sym) = exports.get(right_name)
982 {
983 return Some(member_sym);
984 }
985 }
986
987 None
988 }
989
990 fn get_type_from_mapped_type(&mut self, idx: NodeIndex) -> TypeId {
995 use tsz_parser::parser::NodeIndex as ParserNodeIndex;
996
997 let Some(node) = self.ctx.arena.get(idx) else {
998 return TypeId::ERROR;
999 };
1000
1001 let Some(data) = self.ctx.arena.get_mapped_type(node) else {
1002 return TypeId::ERROR;
1003 };
1004
1005 if data.type_node == ParserNodeIndex::NONE {
1009 let message = "Mapped object type implicitly has an 'any' template type.";
1010 self.ctx
1011 .error(node.pos, node.end - node.pos, message.to_string(), 7039);
1012 return TypeId::ANY;
1013 }
1014
1015 self.lower_with_resolvers(idx, true, false)
1017 }
1018
1019 fn resolve_type_symbol(&self, node_idx: NodeIndex) -> Option<u32> {
1030 use tsz_binder::symbol_flags;
1031 use tsz_solver::is_compiler_managed_type;
1032
1033 let ident = self.ctx.arena.get_identifier_at(node_idx)?;
1034 let name = ident.escaped_text.as_str();
1035
1036 if is_compiler_managed_type(name) {
1037 return None;
1038 }
1039
1040 if let Some(sym_id) = self.ctx.binder.file_locals.get(name) {
1041 let symbol = self.ctx.binder.get_symbol(sym_id)?;
1042 if (symbol.flags
1043 & (symbol_flags::TYPE | symbol_flags::REGULAR_ENUM | symbol_flags::CONST_ENUM))
1044 != 0
1045 {
1046 return Some(sym_id.0);
1047 }
1048 }
1049
1050 for lib_ctx in &self.ctx.lib_contexts {
1051 if let Some(lib_sym_id) = lib_ctx.binder.file_locals.get(name) {
1052 let symbol = lib_ctx.binder.get_symbol(lib_sym_id)?;
1053 if (symbol.flags
1054 & (symbol_flags::TYPE | symbol_flags::REGULAR_ENUM | symbol_flags::CONST_ENUM))
1055 != 0
1056 {
1057 let file_sym_id = self.ctx.binder.file_locals.get(name).unwrap_or(lib_sym_id);
1058 return Some(file_sym_id.0);
1059 }
1060 }
1061 }
1062
1063 None
1064 }
1065
1066 fn resolve_value_symbol(&self, node_idx: NodeIndex) -> Option<u32> {
1071 use tsz_binder::symbol_flags;
1072
1073 let ident = self.ctx.arena.get_identifier_at(node_idx)?;
1074 let name = ident.escaped_text.as_str();
1075
1076 if let Some(sym_id) = self.ctx.binder.file_locals.get(name) {
1077 let symbol = self.ctx.binder.get_symbol(sym_id)?;
1078 if (symbol.flags & (symbol_flags::VALUE | symbol_flags::ALIAS)) != 0 {
1079 return Some(sym_id.0);
1080 }
1081 }
1082
1083 None
1084 }
1085
1086 fn resolve_value_symbol_with_libs(&self, node_idx: NodeIndex) -> Option<u32> {
1091 use tsz_binder::symbol_flags;
1092
1093 let ident = self.ctx.arena.get_identifier_at(node_idx)?;
1094 let name = ident.escaped_text.as_str();
1095
1096 if let Some(sym_id) = self.ctx.binder.file_locals.get(name)
1097 && let Some(symbol) = self.ctx.binder.get_symbol(sym_id)
1098 && (symbol.flags
1099 & (symbol_flags::VALUE
1100 | symbol_flags::ALIAS
1101 | symbol_flags::REGULAR_ENUM
1102 | symbol_flags::CONST_ENUM))
1103 != 0
1104 {
1105 return Some(sym_id.0);
1106 }
1107
1108 for lib_ctx in &self.ctx.lib_contexts {
1109 if let Some(lib_sym_id) = lib_ctx.binder.file_locals.get(name)
1110 && let Some(symbol) = lib_ctx.binder.get_symbol(lib_sym_id)
1111 && (symbol.flags
1112 & (symbol_flags::VALUE
1113 | symbol_flags::ALIAS
1114 | symbol_flags::REGULAR_ENUM
1115 | symbol_flags::CONST_ENUM))
1116 != 0
1117 {
1118 let file_sym_id = self.ctx.binder.file_locals.get(name).unwrap_or(lib_sym_id);
1119 return Some(file_sym_id.0);
1120 }
1121 }
1122
1123 None
1124 }
1125
1126 fn resolve_def_id(&self, node_idx: NodeIndex) -> Option<tsz_solver::def::DefId> {
1128 let sym_id = self.resolve_type_symbol(node_idx)?;
1129 Some(self.ctx.get_or_create_def_id(tsz_binder::SymbolId(sym_id)))
1130 }
1131
1132 fn resolve_def_id_with_qualified_names(
1137 &self,
1138 node_idx: NodeIndex,
1139 ) -> Option<tsz_solver::def::DefId> {
1140 use tsz_parser::parser::syntax_kind_ext;
1141
1142 if let Some(sym_id) = self.resolve_type_symbol(node_idx) {
1143 return Some(self.ctx.get_or_create_def_id(tsz_binder::SymbolId(sym_id)));
1144 }
1145
1146 let node = self.ctx.arena.get(node_idx)?;
1147 if node.kind == syntax_kind_ext::QUALIFIED_NAME {
1148 let qn = self.ctx.arena.get_qualified_name(node)?;
1149 let left_sym_raw = self.resolve_type_symbol(qn.left)?;
1150 let left_sym_id = tsz_binder::SymbolId(left_sym_raw);
1151 let left_symbol = self.ctx.binder.get_symbol(left_sym_id)?;
1152 let right_node = self.ctx.arena.get(qn.right)?;
1153 let right_ident = self.ctx.arena.get_identifier(right_node)?;
1154 let right_name = right_ident.escaped_text.as_str();
1155 let member_sym_id = left_symbol.exports.as_ref()?.get(right_name)?;
1156 return Some(self.ctx.get_or_create_def_id(member_sym_id));
1157 }
1158
1159 None
1160 }
1161
1162 fn collect_type_param_bindings(&self) -> Vec<(tsz_common::interner::Atom, TypeId)> {
1164 self.ctx
1165 .type_parameter_scope
1166 .iter()
1167 .map(|(name, &type_id)| (self.ctx.types.intern_string(name), type_id))
1168 .collect()
1169 }
1170
1171 fn lower_with_resolvers(
1178 &self,
1179 idx: NodeIndex,
1180 use_extended_value_resolver: bool,
1181 use_qualified_names: bool,
1182 ) -> TypeId {
1183 use tsz_lowering::TypeLowering;
1184
1185 let type_param_bindings = self.collect_type_param_bindings();
1186
1187 let type_resolver =
1188 |node_idx: NodeIndex| -> Option<u32> { self.resolve_type_symbol(node_idx) };
1189
1190 let value_resolver = |node_idx: NodeIndex| -> Option<u32> {
1191 if use_extended_value_resolver {
1192 self.resolve_value_symbol_with_libs(node_idx)
1193 } else {
1194 self.resolve_value_symbol(node_idx)
1195 }
1196 };
1197
1198 let def_id_resolver = |node_idx: NodeIndex| -> Option<tsz_solver::def::DefId> {
1199 if use_qualified_names {
1200 self.resolve_def_id_with_qualified_names(node_idx)
1201 } else {
1202 self.resolve_def_id(node_idx)
1203 }
1204 };
1205
1206 let mut lowering = TypeLowering::with_hybrid_resolver(
1207 self.ctx.arena,
1208 self.ctx.types,
1209 &type_resolver,
1210 &def_id_resolver,
1211 &value_resolver,
1212 );
1213 if !type_param_bindings.is_empty() {
1214 lowering = lowering.with_type_param_bindings(type_param_bindings);
1215 }
1216 lowering.lower_type(idx)
1217 }
1218
1219 fn extract_params_from_signature(
1225 &mut self,
1226 sig: &tsz_parser::parser::node::SignatureData,
1227 ) -> (Vec<tsz_solver::ParamInfo>, Option<TypeId>) {
1228 use tsz_solver::ParamInfo;
1229
1230 let mut params = Vec::new();
1231 let mut this_type = None;
1232
1233 if let Some(ref param_list) = sig.parameters {
1234 for ¶m_idx in ¶m_list.nodes {
1235 let Some(param_node) = self.ctx.arena.get(param_idx) else {
1236 continue;
1237 };
1238 let Some(param_data) = self.ctx.arena.get_parameter(param_node) else {
1239 continue;
1240 };
1241
1242 let name = self.get_param_name(param_data.name);
1244
1245 if name == "this" {
1247 this_type = (param_data.type_annotation.is_some())
1248 .then(|| self.check(param_data.type_annotation));
1249 continue;
1250 }
1251
1252 let type_id = if param_data.type_annotation.is_some() {
1254 self.check(param_data.type_annotation)
1255 } else {
1256 TypeId::ANY
1257 };
1258
1259 let optional = param_data.question_token || param_data.initializer.is_some();
1260 let rest = param_data.dot_dot_dot_token;
1261
1262 let effective_type = if param_data.question_token
1265 && self.ctx.strict_null_checks()
1266 && type_id != TypeId::ANY
1267 && type_id != TypeId::ERROR
1268 && type_id != TypeId::UNDEFINED
1269 {
1270 let factory = self.ctx.types.factory();
1271 factory.union(vec![type_id, TypeId::UNDEFINED])
1272 } else {
1273 type_id
1274 };
1275
1276 params.push(ParamInfo {
1277 name: Some(self.ctx.types.intern_string(&name)),
1278 type_id: effective_type,
1279 optional,
1280 rest,
1281 });
1282 }
1283 }
1284
1285 (params, this_type)
1286 }
1287
1288 fn resolve_return_type_with_params_in_scope(
1293 &mut self,
1294 type_annotation: NodeIndex,
1295 params: &[tsz_solver::ParamInfo],
1296 ) -> TypeId {
1297 if type_annotation.is_none() {
1298 return TypeId::ANY;
1299 }
1300
1301 for param in params {
1303 if let Some(name_atom) = param.name {
1304 let name = self.ctx.types.resolve_atom(name_atom);
1305 self.ctx.typeof_param_scope.insert(name, param.type_id);
1306 }
1307 }
1308
1309 let return_type = self.check(type_annotation);
1310
1311 for param in params {
1313 if let Some(name_atom) = param.name {
1314 let name = self.ctx.types.resolve_atom(name_atom);
1315 self.ctx.typeof_param_scope.remove(&name);
1316 }
1317 }
1318
1319 return_type
1320 }
1321
1322 fn get_param_name(&self, name_idx: NodeIndex) -> String {
1324 if let Some(ident) = self.ctx.arena.get_identifier_at(name_idx) {
1325 return ident.escaped_text.to_string();
1326 }
1327 "_".to_string()
1328 }
1329
1330 fn get_property_name(&self, name_idx: NodeIndex) -> Option<String> {
1332 use tsz_scanner::SyntaxKind;
1333
1334 let name_node = self.ctx.arena.get(name_idx)?;
1335
1336 if let Some(ident) = self.ctx.arena.get_identifier(name_node) {
1338 return Some(ident.escaped_text.clone());
1339 }
1340
1341 if matches!(
1343 name_node.kind,
1344 k if k == SyntaxKind::StringLiteral as u16
1345 || k == SyntaxKind::NoSubstitutionTemplateLiteral as u16
1346 || k == SyntaxKind::NumericLiteral as u16
1347 ) && let Some(lit) = self.ctx.arena.get_literal(name_node)
1348 {
1349 if name_node.kind == SyntaxKind::NumericLiteral as u16
1351 && let Some(canonical) = tsz_solver::utils::canonicalize_numeric_name(&lit.text)
1352 {
1353 return Some(canonical);
1354 }
1355 return Some(lit.text.clone());
1356 }
1357
1358 None
1359 }
1360
1361 fn has_readonly_modifier(&self, modifiers: &Option<tsz_parser::parser::NodeList>) -> bool {
1363 self.ctx
1364 .arena
1365 .has_modifier(modifiers, tsz_scanner::SyntaxKind::ReadonlyKeyword)
1366 }
1367
1368 pub const fn context(&self) -> &CheckerContext<'ctx> {
1370 self.ctx
1371 }
1372}
1373
1374#[cfg(test)]
1375#[path = "../../tests/type_node.rs"]
1376mod tests;
1377
1378pub(crate) fn check_duplicate_parameters_in_type(
1380 ctx: &mut crate::CheckerContext,
1381 parameters: &tsz_parser::parser::NodeList,
1382) {
1383 let mut seen_names = rustc_hash::FxHashSet::default();
1384 for ¶m_idx in ¶meters.nodes {
1385 if let Some(param_node) = ctx.arena.get(param_idx)
1386 && let Some(param) = ctx.arena.get_parameter(param_node)
1387 {
1388 collect_names_in_type(ctx, param.name, &mut seen_names);
1389 }
1390 }
1391}
1392
1393fn collect_names_in_type(
1394 ctx: &mut crate::CheckerContext,
1395 name_idx: tsz_parser::parser::NodeIndex,
1396 seen: &mut rustc_hash::FxHashSet<String>,
1397) {
1398 use tsz_scanner::SyntaxKind;
1399 let Some(node) = ctx.arena.get(name_idx) else {
1400 return;
1401 };
1402 if node.kind == SyntaxKind::Identifier as u16 {
1403 if let Some(name) = ctx
1404 .arena
1405 .get_identifier(node)
1406 .map(|i| i.escaped_text.clone())
1407 && !seen.insert(name.clone())
1408 {
1409 let msg = crate::diagnostics::format_message(
1410 crate::diagnostics::diagnostic_messages::DUPLICATE_IDENTIFIER,
1411 &[&name],
1412 );
1413 ctx.error(
1414 node.pos,
1415 node.end - node.pos,
1416 msg,
1417 crate::diagnostics::diagnostic_codes::DUPLICATE_IDENTIFIER,
1418 );
1419 }
1420 } else if (node.kind == tsz_parser::parser::syntax_kind_ext::OBJECT_BINDING_PATTERN
1421 || node.kind == tsz_parser::parser::syntax_kind_ext::ARRAY_BINDING_PATTERN)
1422 && let Some(pattern) = ctx.arena.get_binding_pattern(node)
1423 {
1424 for &elem_idx in &pattern.elements.nodes {
1425 if let Some(elem_node) = ctx.arena.get(elem_idx) {
1426 if elem_node.kind == tsz_parser::parser::syntax_kind_ext::OMITTED_EXPRESSION {
1427 continue;
1428 }
1429 if let Some(elem) = ctx.arena.get_binding_element(elem_node) {
1430 if elem.property_name.is_some()
1431 && let Some(prop_node) = ctx.arena.get(elem.property_name)
1432 && prop_node.kind == SyntaxKind::Identifier as u16
1433 && let Some(name_node) = ctx.arena.get(elem.name)
1434 && name_node.kind == SyntaxKind::Identifier as u16
1435 {
1436 let prop_name = ctx
1437 .arena
1438 .get_identifier(prop_node)
1439 .map(|i| i.escaped_text.trim_end_matches(":").trim().to_string())
1440 .unwrap_or_default();
1441 let name_str = ctx
1442 .arena
1443 .get_identifier(name_node)
1444 .map(|i| i.escaped_text.clone())
1445 .unwrap_or_default();
1446 let msg = crate::diagnostics::format_message(crate::diagnostics::diagnostic_messages::IS_AN_UNUSED_RENAMING_OF_DID_YOU_INTEND_TO_USE_IT_AS_A_TYPE_ANNOTATION, &[&name_str, &prop_name]);
1447 ctx.error(name_node.pos, name_node.end - name_node.pos, msg, crate::diagnostics::diagnostic_codes::IS_AN_UNUSED_RENAMING_OF_DID_YOU_INTEND_TO_USE_IT_AS_A_TYPE_ANNOTATION);
1448 }
1449 collect_names_in_type(ctx, elem.name, seen);
1450 }
1451 }
1452 }
1453 }
1454}
1455
1456impl<'a, 'ctx> TypeNodeChecker<'a, 'ctx> {
1457 fn is_this_type_allowed(&self, this_node_idx: tsz_parser::parser::NodeIndex) -> bool {
1458 use tsz_parser::parser::syntax_kind_ext;
1459
1460 let mut child_idx = this_node_idx;
1461 let mut current = self
1462 .ctx
1463 .arena
1464 .get_extended(this_node_idx)
1465 .map(|ext| ext.parent);
1466
1467 while let Some(parent_idx) = current {
1468 if parent_idx.is_none() {
1469 break;
1470 }
1471 let Some(node) = self.ctx.arena.get(parent_idx) else {
1472 break;
1473 };
1474
1475 match node.kind {
1476 syntax_kind_ext::CLASS_DECLARATION
1478 | syntax_kind_ext::CLASS_EXPRESSION
1479 | syntax_kind_ext::INTERFACE_DECLARATION => {
1480 return true;
1481 }
1482
1483 syntax_kind_ext::METHOD_DECLARATION
1485 | syntax_kind_ext::PROPERTY_DECLARATION
1486 | syntax_kind_ext::GET_ACCESSOR
1487 | syntax_kind_ext::SET_ACCESSOR
1488 | syntax_kind_ext::INDEX_SIGNATURE
1489 | syntax_kind_ext::PROPERTY_SIGNATURE
1490 | syntax_kind_ext::METHOD_SIGNATURE => {
1491 let is_static = (node.flags & tsz_parser::modifier_flags::STATIC as u16) != 0;
1493 if is_static {
1494 return false;
1495 }
1496 }
1499
1500 syntax_kind_ext::CONSTRUCTOR => {
1502 if let Some(c) = self.ctx.arena.get_constructor(node)
1505 && child_idx == c.body
1506 {
1507 return true; }
1509 return false;
1510 }
1511
1512 syntax_kind_ext::FUNCTION_DECLARATION
1513 | syntax_kind_ext::FUNCTION_EXPRESSION
1514 | syntax_kind_ext::TYPE_ALIAS_DECLARATION
1515 | syntax_kind_ext::MODULE_DECLARATION
1516 | syntax_kind_ext::TYPE_LITERAL
1517 | syntax_kind_ext::OBJECT_LITERAL_EXPRESSION
1518 | syntax_kind_ext::CLASS_STATIC_BLOCK_DECLARATION => {
1519 return false;
1520 }
1521
1522 _ => {}
1525 }
1526
1527 child_idx = parent_idx;
1528 current = self
1529 .ctx
1530 .arena
1531 .get_extended(parent_idx)
1532 .map(|ext| ext.parent);
1533 }
1534
1535 false
1536 }
1537}
1538pub(crate) fn check_parameter_initializers_in_type(
1539 ctx: &mut crate::CheckerContext,
1540 parameters: &tsz_parser::parser::NodeList,
1541) {
1542 for ¶m_idx in ¶meters.nodes {
1543 if let Some(param_node) = ctx.arena.get(param_idx)
1544 && let Some(param) = ctx.arena.get_parameter(param_node)
1545 && param.initializer.is_some()
1546 {
1547 let name_node = ctx.arena.get(param.name).unwrap_or(param_node);
1549 ctx.error(
1550 name_node.pos,
1551 name_node.end - name_node.pos,
1552 "A parameter initializer is only allowed in a function or constructor implementation."
1553 .to_string(),
1554 2371,
1555 );
1556 }
1557 }
1558}