From 375e62ebbdc717cb8fa941f1ee5ebbb66fdac406 Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Fri, 6 Mar 2026 09:39:58 +0100 Subject: [PATCH 1/2] refactor: add type resolution to TsTypeRefDef --- js/types.d.ts | 21 ++ src/function.rs | 1 + src/lib.rs | 61 +++- src/ts_type.rs | 296 ++++++++++++++++++ tests/diff_specs/class_implements_added.txt | 5 +- .../diff_specs/class_static_method_added.txt | 5 +- .../interface_constructor_change.txt | 10 +- tests/diff_specs/interface_extends_added.txt | 5 +- tests/diff_specs/type_param_added.txt | 14 +- tests/diff_specs/type_param_removed.txt | 14 +- .../class_generic_extends_implements.txt | 28 +- tests/specs/export_fn2.txt | 5 +- tests/specs/export_interface.txt | 10 +- tests/specs/export_interface2.txt | 7 +- tests/specs/export_type_alias_literal.txt | 5 +- tests/specs/function_generic.txt | 14 +- .../generic_instantiated_with_tuple_type.txt | 5 +- tests/specs/import_types.txt | 7 +- tests/specs/infer_types.txt | 14 +- tests/specs/interface_generic_extends.txt | 7 +- tests/specs/interface_index_signature.txt | 5 +- .../interface_readonly_index_signature.txt | 5 +- tests/specs/internal_tag.txt | 10 +- tests/specs/mapped_types.txt | 19 +- tests/specs/namespace_exports_self.txt | 5 +- tests/specs/namespace_reexport_member.txt | 14 +- .../private_type_implementation_signature.txt | 15 +- tests/specs/private_type_in_namespace.txt | 5 +- tests/specs/private_type_multiple_refs.txt | 14 +- tests/specs/private_type_other_module.txt | 7 +- tests/specs/private_type_private_member.txt | 10 +- .../private_type_re_export_referencing.txt | 15 +- ...vate_type_referenced_from_class_member.txt | 5 +- .../re_export_clashes_private_type_bug.txt | 10 +- tests/specs/reexport_deep.txt | 7 +- ...remote_module_re_export_no_diagnostics.txt | 10 +- tests/specs/ts_user_defined_type_guards.txt | 14 +- tests/specs/type_alias_infer_type.txt | 14 +- tests/specs/type_generic_alias.txt | 7 +- tests/specs/type_import_type.txt | 7 +- tests/specs/type_literal_mapped_type.txt | 19 +- 41 files changed, 684 insertions(+), 67 deletions(-) diff --git a/js/types.d.ts b/js/types.d.ts index a646fafa..b67c729c 100644 --- a/js/types.d.ts +++ b/js/types.d.ts @@ -755,9 +755,30 @@ export type TsTypeDefKind = | "typeLiteral" | "typePredicate"; +export type TypeParamDeclaringKind = + | "class" + | "interface" + | "function" + | "typeAlias" + | "method"; + +export type TypeRefResolution = + | { kind: "local" } + | { + kind: "typeParam"; + declaringName?: string; + declaringKind?: TypeParamDeclaringKind; + } + | { + kind: "import"; + specifier: string; + name?: string; + }; + export interface TsTypeRefDef { typeParams?: TsTypeDef[]; typeName: string; + resolution?: TypeRefResolution; } export interface TypeAliasDef { diff --git a/src/function.rs b/src/function.rs index d5674581..05ee1e45 100644 --- a/src/function.rs +++ b/src/function.rs @@ -60,6 +60,7 @@ pub fn function_to_function_def( crate::ts_type::TsTypeRefDef { type_params: Some(Box::new([TsTypeDef::keyword("void")])), type_name: "Promise".to_string(), + resolution: None, }, ), }) diff --git a/src/lib.rs b/src/lib.rs index c85c26a2..9b7b9ad8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,15 +126,17 @@ fn get_children_of_node(node: Symbol) -> Vec { doc_nodes } -pub fn docnodes_v1_to_v2(value: serde_json::Value) -> Vec { +pub fn docnodes_v1_to_v2(value: serde_json::Value) -> Document { let serde_json::Value::Array(arr) = value else { - return vec![]; + return Document::default(); }; // v1 format: flat array where each entry has "name", "kind", "location", // "declarationKind", "jsDoc", and def fields all at the top level. - // v2 format: array of Symbol { name, isDefault, declarations: [...] } - // where each declaration has the remaining fields. + // v2 format: Document { module_doc, imports, symbols } + // where symbols are Symbol { name, isDefault, declarations: [...] }. + let mut module_doc = js_doc::JsDoc::default(); + let mut imports = Vec::new(); let mut symbols: indexmap::IndexMap, Symbol> = indexmap::IndexMap::new(); @@ -143,6 +145,51 @@ pub fn docnodes_v1_to_v2(value: serde_json::Value) -> Vec { continue; }; + let kind = obj + .get("kind") + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + + // v1 moduleDoc nodes become Document.module_doc + if kind.as_deref() == Some("moduleDoc") { + if let Some(js_doc_val) = obj + .remove("jsDoc") + .and_then(|v| serde_json::from_value::(v).ok()) + { + module_doc = js_doc_val; + } + continue; + } + + // v1 import nodes become Document.imports + if kind.as_deref() == Some("import") { + let imported_name: Box = obj + .remove("name") + .and_then(|v| v.as_str().map(|s| s.into())) + .unwrap_or_else(|| "".into()); + let import_def = obj.remove("importDef").unwrap_or_default(); + let src = import_def + .get("src") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + let original_name = import_def + .get("imported") + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + let js_doc = obj + .remove("jsDoc") + .and_then(|v| serde_json::from_value::(v).ok()) + .unwrap_or_default(); + imports.push(node::Import { + imported_name, + original_name, + src, + js_doc, + }); + continue; + } + let name: Box = obj .remove("name") .and_then(|v| v.as_str().map(|s| s.into())) @@ -190,7 +237,11 @@ pub fn docnodes_v1_to_v2(value: serde_json::Value) -> Vec { } } - symbols.into_values().collect() + Document { + module_doc, + imports, + symbols: symbols.into_values().collect(), + } } /// Recursively walk JSON and convert v1 TsTypeDef objects (which use diff --git a/src/ts_type.rs b/src/ts_type.rs index 8123d695..949450a0 100644 --- a/src/ts_type.rs +++ b/src/ts_type.rs @@ -249,11 +249,15 @@ impl TsTypeDef { None }; + let root_ident = ts_entity_name_root_ident(&other.type_name); + let resolution = resolve_type_ref(module_info, root_ident); + TsTypeDef { repr: type_name.clone(), kind: TsTypeDefKind::TypeRef(TsTypeRefDef { type_params, type_name, + resolution, }), } } @@ -276,11 +280,15 @@ impl TsTypeDef { None }; + let resolution = expr_root_ident(&other.expr) + .and_then(|i| resolve_type_ref(module_info, i)); + TsTypeDef { repr: type_name.clone(), kind: TsTypeDefKind::TypeRef(TsTypeRefDef { type_params, type_name, + resolution, }), } } @@ -723,11 +731,297 @@ fn ts_entity_name_to_name(entity_name: &TsEntityName) -> String { } } +fn ts_entity_name_root_ident(entity_name: &TsEntityName) -> &Ident { + match entity_name { + TsEntityName::Ident(ident) => ident, + TsEntityName::TsQualifiedName(ts_qualified_name) => { + ts_entity_name_root_ident(&ts_qualified_name.left) + } + } +} + +fn expr_root_ident(expr: &Expr) -> Option<&Ident> { + match expr { + Expr::Ident(ident) => Some(ident), + Expr::Member(member_expr) => expr_root_ident(&member_expr.obj), + _ => None, + } +} + +fn resolve_type_ref( + module_info: &EsModuleInfo, + ident: &Ident, +) -> Option { + if let Some(symbol) = module_info.symbol_from_swc(&ident.to_id()) { + if let Some(file_dep) = symbol.file_dep() { + Some(TypeRefResolution::Import { + specifier: file_dep.specifier.clone(), + name: file_dep.name.maybe_name().map(|s| s.to_string()), + }) + } else { + Some(TypeRefResolution::Local) + } + } else if ident.ctxt != module_info.source().unresolved_context() { + // The identifier has a resolved syntax context but is not a module-level + // symbol — it's a type parameter or other locally-scoped binding. + let origin = find_type_param_origin(module_info, &ident.to_id()); + Some(TypeRefResolution::TypeParam { + declaring_name: origin.as_ref().map(|o| o.0.clone()), + declaring_kind: origin.as_ref().map(|o| o.1.clone()), + }) + } else { + // Unresolved identifier — a global type (Promise, Array, etc.) + // or truly unresolvable. + None + } +} + +/// Walks the module AST to find which declaration owns a type parameter. +fn find_type_param_origin( + module_info: &EsModuleInfo, + target_id: &Id, +) -> Option<(String, TypeParamDeclaringKind)> { + use deno_ast::ModuleItemRef; + + let program = module_info.source().program_ref(); + + for item in program.body() { + match item { + ModuleItemRef::ModuleDecl(ModuleDecl::ExportDecl(export)) => { + if let Some(origin) = find_type_param_in_decl(&export.decl, target_id) + { + return Some(origin); + } + } + ModuleItemRef::Stmt(Stmt::Decl(decl)) => { + if let Some(origin) = find_type_param_in_decl(decl, target_id) { + return Some(origin); + } + } + ModuleItemRef::ModuleDecl(ModuleDecl::ExportDefaultDecl(export)) => { + match &export.decl { + DefaultDecl::Class(class_expr) => { + if let Some(origin) = find_type_param_in_class( + &class_expr.class, + class_expr + .ident + .as_ref() + .map(|i| i.sym.as_ref()) + .unwrap_or("default"), + target_id, + ) { + return Some(origin); + } + } + DefaultDecl::Fn(fn_expr) => { + if has_type_param(&fn_expr.function.type_params, target_id) { + let name = fn_expr + .ident + .as_ref() + .map(|i| i.sym.to_string()) + .unwrap_or_else(|| "default".to_string()); + return Some((name, TypeParamDeclaringKind::Function)); + } + } + DefaultDecl::TsInterfaceDecl(iface) => { + if let Some(origin) = + find_type_param_in_interface(iface, target_id) + { + return Some(origin); + } + } + } + } + _ => {} + } + } + + None +} + +fn find_type_param_in_decl( + decl: &Decl, + target_id: &Id, +) -> Option<(String, TypeParamDeclaringKind)> { + match decl { + Decl::Class(class_decl) => find_type_param_in_class( + &class_decl.class, + class_decl.ident.sym.as_ref(), + target_id, + ), + Decl::Fn(fn_decl) => { + if has_type_param(&fn_decl.function.type_params, target_id) { + Some(( + fn_decl.ident.sym.to_string(), + TypeParamDeclaringKind::Function, + )) + } else { + None + } + } + Decl::TsInterface(iface) => { + find_type_param_in_interface(iface, target_id) + } + Decl::TsTypeAlias(alias) => { + if has_type_param(&alias.type_params, target_id) { + Some(( + alias.id.sym.to_string(), + TypeParamDeclaringKind::TypeAlias, + )) + } else { + None + } + } + Decl::TsModule(ts_module) => { + // Check declarations inside namespaces + if let Some(TsNamespaceBody::TsModuleBlock(block)) = &ts_module.body { + for item in &block.body { + let inner_decl = match item { + ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export)) => { + Some(&export.decl) + } + ModuleItem::Stmt(Stmt::Decl(decl)) => Some(decl), + _ => None, + }; + if let Some(origin) = inner_decl.and_then(|inner_decl| find_type_param_in_decl(inner_decl, target_id)) { + return Some(origin); + } + } + } + None + } + _ => None, + } +} + +fn find_type_param_in_class( + class: &Class, + class_name: &str, + target_id: &Id, +) -> Option<(String, TypeParamDeclaringKind)> { + if has_type_param(&class.type_params, target_id) { + return Some((class_name.to_string(), TypeParamDeclaringKind::Class)); + } + + // Check method type params + for member in &class.body { + if let ClassMember::Method(method) = member && has_type_param(&method.function.type_params, target_id) { + let method_name = simple_prop_name(&method.key); + return Some((method_name, TypeParamDeclaringKind::Method)); + } + } + + None +} + +fn find_type_param_in_interface( + iface: &TsInterfaceDecl, + target_id: &Id, +) -> Option<(String, TypeParamDeclaringKind)> { + if has_type_param(&iface.type_params, target_id) { + return Some(( + iface.id.sym.to_string(), + TypeParamDeclaringKind::Interface, + )); + } + + // Check method type params + for member in &iface.body.body { + match member { + TsTypeElement::TsMethodSignature(method) => { + if has_type_param(&method.type_params, target_id) { + let method_name = simple_expr_name(&method.key); + return Some((method_name, TypeParamDeclaringKind::Method)); + } + } + TsTypeElement::TsConstructSignatureDecl(ctor) => { + if has_type_param(&ctor.type_params, target_id) { + return Some(("new".to_string(), TypeParamDeclaringKind::Method)); + } + } + TsTypeElement::TsCallSignatureDecl(call) => { + if has_type_param(&call.type_params, target_id) { + return Some(("call".to_string(), TypeParamDeclaringKind::Method)); + } + } + _ => {} + } + } + + None +} + +fn simple_prop_name(prop_name: &PropName) -> String { + match prop_name { + PropName::Ident(ident) => ident.sym.to_string(), + PropName::Str(str_) => str_.value.to_string_lossy().into_owned(), + PropName::Num(num) => num.value.to_string(), + PropName::BigInt(num) => num.value.to_string(), + PropName::Computed(_) => "[computed]".to_string(), + } +} + +fn simple_expr_name(expr: &Expr) -> String { + match expr { + Expr::Ident(ident) => ident.sym.to_string(), + _ => "[computed]".to_string(), + } +} + +fn has_type_param( + type_params: &Option>, + target_id: &Id, +) -> bool { + type_params + .as_ref() + .is_some_and(|tp| tp.params.iter().any(|p| p.name.to_id() == *target_id)) +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub enum TypeParamDeclaringKind { + Class, + Interface, + Function, + TypeAlias, + Method, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(tag = "kind", rename_all = "camelCase")] +pub enum TypeRefResolution { + /// Resolves to a symbol defined locally in this module. + Local, + /// Resolves to a type parameter in the enclosing declaration. + #[serde(rename_all = "camelCase")] + TypeParam { + /// Name of the declaration that declares this type parameter. + #[serde(skip_serializing_if = "Option::is_none", default)] + declaring_name: Option, + /// Kind of the declaration that declares this type parameter. + #[serde(skip_serializing_if = "Option::is_none", default)] + declaring_kind: Option, + }, + /// Resolves to a symbol imported from another module. + #[serde(rename_all = "camelCase")] + Import { + /// The raw import specifier (e.g. `"./bar.ts"` or `"https://..."`). + specifier: String, + /// The name of the symbol in the source module + /// (e.g. `"Foo"` for `import { Foo }`, `"default"` for default imports, + /// or `None` for namespace imports like `import * as ns`). + #[serde(skip_serializing_if = "Option::is_none", default)] + name: Option, + }, +} + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct TsTypeRefDef { pub type_params: Option>, pub type_name: String, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub resolution: Option, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] @@ -1321,6 +1615,7 @@ impl TsTypeDef { kind: TsTypeDefKind::TypeRef(TsTypeRefDef { type_params: None, type_name: "RegExp".to_string(), + resolution: None, }), } } @@ -1682,6 +1977,7 @@ fn infer_ts_type_from_new_expr( maybe_type_param_instantiation_to_type_defs(module_info, Some(init)) }), type_name: ident.sym.to_string(), + resolution: resolve_type_ref(module_info, ident), }), }), _ => None, diff --git a/tests/diff_specs/class_implements_added.txt b/tests/diff_specs/class_implements_added.txt index a5c33da2..3dd5f799 100644 --- a/tests/diff_specs/class_implements_added.txt +++ b/tests/diff_specs/class_implements_added.txt @@ -73,7 +73,10 @@ export class Foo implements Runnable { "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Runnable" + "typeName": "Runnable", + "resolution": { + "kind": "local" + } } } ] diff --git a/tests/diff_specs/class_static_method_added.txt b/tests/diff_specs/class_static_method_added.txt index 02a54ac6..9cdd203f 100644 --- a/tests/diff_specs/class_static_method_added.txt +++ b/tests/diff_specs/class_static_method_added.txt @@ -38,7 +38,10 @@ export class Foo { "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Foo" + "typeName": "Foo", + "resolution": { + "kind": "local" + } } }, "hasBody": true, diff --git a/tests/diff_specs/interface_constructor_change.txt b/tests/diff_specs/interface_constructor_change.txt index c2945207..e6a03feb 100644 --- a/tests/diff_specs/interface_constructor_change.txt +++ b/tests/diff_specs/interface_constructor_change.txt @@ -51,7 +51,10 @@ export interface Foo { "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Foo" + "typeName": "Foo", + "resolution": { + "kind": "local" + } } }, "typeParams": [], @@ -82,7 +85,10 @@ export interface Foo { "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Foo" + "typeName": "Foo", + "resolution": { + "kind": "local" + } } }, "typeParams": [], diff --git a/tests/diff_specs/interface_extends_added.txt b/tests/diff_specs/interface_extends_added.txt index 8e4e2b2a..2dfc82ff 100644 --- a/tests/diff_specs/interface_extends_added.txt +++ b/tests/diff_specs/interface_extends_added.txt @@ -75,7 +75,10 @@ export interface Foo extends Base { "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Base" + "typeName": "Base", + "resolution": { + "kind": "local" + } } } ] diff --git a/tests/diff_specs/type_param_added.txt b/tests/diff_specs/type_param_added.txt index 87d8acf4..f26a4d91 100644 --- a/tests/diff_specs/type_param_added.txt +++ b/tests/diff_specs/type_param_added.txt @@ -32,7 +32,12 @@ export function identity(value: T): T { return value; } "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "identity", + "declaringKind": "function" + } } } } @@ -50,7 +55,12 @@ export function identity(value: T): T { return value; } "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "identity", + "declaringKind": "function" + } } } }, diff --git a/tests/diff_specs/type_param_removed.txt b/tests/diff_specs/type_param_removed.txt index 13f6f7e0..3d4141ad 100644 --- a/tests/diff_specs/type_param_removed.txt +++ b/tests/diff_specs/type_param_removed.txt @@ -27,7 +27,12 @@ export function identity(value: unknown): unknown { return value; } "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "identity", + "declaringKind": "function" + } } }, "new": { @@ -45,7 +50,12 @@ export function identity(value: unknown): unknown { return value; } "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "identity", + "declaringKind": "function" + } } }, "new": { diff --git a/tests/specs/class_generic_extends_implements.txt b/tests/specs/class_generic_extends_implements.txt index 71f6d117..b1c52d56 100644 --- a/tests/specs/class_generic_extends_implements.txt +++ b/tests/specs/class_generic_extends_implements.txt @@ -49,7 +49,12 @@ class Class extends Map implements Iterator, Iterable "kind": "typeRef", "value": { "typeParams": null, - "typeName": "A" + "typeName": "A", + "resolution": { + "kind": "typeParam", + "declaringName": "Class", + "declaringKind": "class" + } } } ], @@ -66,7 +71,12 @@ class Class extends Map implements Iterator, Iterable "kind": "typeRef", "value": { "typeParams": null, - "typeName": "B" + "typeName": "B", + "resolution": { + "kind": "typeParam", + "declaringName": "Class", + "declaringKind": "class" + } } } ], @@ -88,7 +98,12 @@ class Class extends Map implements Iterator, Iterable "kind": "typeRef", "value": { "typeParams": null, - "typeName": "A" + "typeName": "A", + "resolution": { + "kind": "typeParam", + "declaringName": "Class", + "declaringKind": "class" + } } }, { @@ -96,7 +111,12 @@ class Class extends Map implements Iterator, Iterable "kind": "typeRef", "value": { "typeParams": null, - "typeName": "B" + "typeName": "B", + "resolution": { + "kind": "typeParam", + "declaringName": "Class", + "declaringKind": "class" + } } } ] diff --git a/tests/specs/export_fn2.txt b/tests/specs/export_fn2.txt index 53fdf15b..9bdf468f 100644 --- a/tests/specs/export_fn2.txt +++ b/tests/specs/export_fn2.txt @@ -143,7 +143,10 @@ private interface AssignOpts "kind": "typeRef", "value": { "typeParams": null, - "typeName": "AssignOpts" + "typeName": "AssignOpts", + "resolution": { + "kind": "local" + } } } }, diff --git a/tests/specs/export_interface.txt b/tests/specs/export_interface.txt index be06c759..28045877 100644 --- a/tests/specs/export_interface.txt +++ b/tests/specs/export_interface.txt @@ -88,7 +88,10 @@ interface Reader extends Foo, Bar "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Foo" + "typeName": "Foo", + "resolution": { + "kind": "local" + } } }, { @@ -96,7 +99,10 @@ interface Reader extends Foo, Bar "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Bar" + "typeName": "Bar", + "resolution": { + "kind": "local" + } } } ], diff --git a/tests/specs/export_interface2.txt b/tests/specs/export_interface2.txt index 220f0e26..228acedf 100644 --- a/tests/specs/export_interface2.txt +++ b/tests/specs/export_interface2.txt @@ -61,7 +61,12 @@ interface TypedIface "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "TypedIface", + "declaringKind": "interface" + } } }, "typeParams": [] diff --git a/tests/specs/export_type_alias_literal.txt b/tests/specs/export_type_alias_literal.txt index b2c06c7d..160639c0 100644 --- a/tests/specs/export_type_alias_literal.txt +++ b/tests/specs/export_type_alias_literal.txt @@ -60,7 +60,10 @@ type A = { a(): void; b?(): void; c(): string; c(v: number); } "kind": "typeRef", "value": { "typeParams": null, - "typeName": "A" + "typeName": "A", + "resolution": { + "kind": "local" + } } }, "typeParams": [], diff --git a/tests/specs/function_generic.txt b/tests/specs/function_generic.txt index feaa1d20..b7e14076 100644 --- a/tests/specs/function_generic.txt +++ b/tests/specs/function_generic.txt @@ -48,7 +48,12 @@ function add(a: T, b: T) "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "add", + "declaringKind": "function" + } } } }, @@ -61,7 +66,12 @@ function add(a: T, b: T) "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "add", + "declaringKind": "function" + } } } } diff --git a/tests/specs/generic_instantiated_with_tuple_type.txt b/tests/specs/generic_instantiated_with_tuple_type.txt index 56958fe3..21eaa854 100644 --- a/tests/specs/generic_instantiated_with_tuple_type.txt +++ b/tests/specs/generic_instantiated_with_tuple_type.txt @@ -95,7 +95,10 @@ interface Generic ] } ], - "typeName": "Generic" + "typeName": "Generic", + "resolution": { + "kind": "local" + } } }, "hasBody": true, diff --git a/tests/specs/import_types.txt b/tests/specs/import_types.txt index 82e6ea59..3a046487 100644 --- a/tests/specs/import_types.txt +++ b/tests/specs/import_types.txt @@ -50,7 +50,12 @@ function adopt(p: import("./module.ts").Pet): void "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "adopt", + "declaringKind": "function" + } } } ] diff --git a/tests/specs/infer_types.txt b/tests/specs/infer_types.txt index a58920d1..739052b7 100644 --- a/tests/specs/infer_types.txt +++ b/tests/specs/infer_types.txt @@ -40,7 +40,12 @@ type Flatten = T extends Array ? U : T "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "Flatten", + "declaringKind": "typeAlias" + } } }, "extendsType": { @@ -74,7 +79,12 @@ type Flatten = T extends Array ? U : T "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "Flatten", + "declaringKind": "typeAlias" + } } } } diff --git a/tests/specs/interface_generic_extends.txt b/tests/specs/interface_generic_extends.txt index c0c0f2b1..07f1ad67 100644 --- a/tests/specs/interface_generic_extends.txt +++ b/tests/specs/interface_generic_extends.txt @@ -43,7 +43,12 @@ interface Interface extends Iterable "kind": "typeRef", "value": { "typeParams": null, - "typeName": "V" + "typeName": "V", + "resolution": { + "kind": "typeParam", + "declaringName": "Interface", + "declaringKind": "interface" + } } } ], diff --git a/tests/specs/interface_index_signature.txt b/tests/specs/interface_index_signature.txt index 0f267bc3..88c90f2e 100644 --- a/tests/specs/interface_index_signature.txt +++ b/tests/specs/interface_index_signature.txt @@ -67,7 +67,10 @@ interface Interface "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Interface" + "typeName": "Interface", + "resolution": { + "kind": "local" + } } }, "location": { diff --git a/tests/specs/interface_readonly_index_signature.txt b/tests/specs/interface_readonly_index_signature.txt index 4c1e3041..7828aa5f 100644 --- a/tests/specs/interface_readonly_index_signature.txt +++ b/tests/specs/interface_readonly_index_signature.txt @@ -67,7 +67,10 @@ interface Interface "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Interface" + "typeName": "Interface", + "resolution": { + "kind": "local" + } } }, "location": { diff --git a/tests/specs/internal_tag.txt b/tests/specs/internal_tag.txt index ce801919..107c97b6 100644 --- a/tests/specs/internal_tag.txt +++ b/tests/specs/internal_tag.txt @@ -123,7 +123,10 @@ type Test = OtherPrivateType "kind": "typeRef", "value": { "typeParams": null, - "typeName": "PrivateType" + "typeName": "PrivateType", + "resolution": { + "kind": "local" + } } }, "readonly": false, @@ -273,7 +276,10 @@ type Test = OtherPrivateType "kind": "typeRef", "value": { "typeParams": null, - "typeName": "OtherPrivateType" + "typeName": "OtherPrivateType", + "resolution": { + "kind": "local" + } } }, "typeParams": [] diff --git a/tests/specs/mapped_types.txt b/tests/specs/mapped_types.txt index 49c8b1dc..47fa59e2 100644 --- a/tests/specs/mapped_types.txt +++ b/tests/specs/mapped_types.txt @@ -50,7 +50,12 @@ type MappedTypeWithNewProperties = readonly [Properties in keyof Type as N "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Type" + "typeName": "Type", + "resolution": { + "kind": "typeParam", + "declaringName": "MappedTypeWithNewProperties", + "declaringKind": "typeAlias" + } } } } @@ -75,7 +80,12 @@ type MappedTypeWithNewProperties = readonly [Properties in keyof Type as N "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Type" + "typeName": "Type", + "resolution": { + "kind": "typeParam", + "declaringName": "MappedTypeWithNewProperties", + "declaringKind": "typeAlias" + } } }, "indexType": { @@ -83,7 +93,10 @@ type MappedTypeWithNewProperties = readonly [Properties in keyof Type as N "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Properties" + "typeName": "Properties", + "resolution": { + "kind": "typeParam" + } } } } diff --git a/tests/specs/namespace_exports_self.txt b/tests/specs/namespace_exports_self.txt index 5a2e3f20..e8234b8d 100644 --- a/tests/specs/namespace_exports_self.txt +++ b/tests/specs/namespace_exports_self.txt @@ -297,7 +297,10 @@ namespace f "kind": "typeRef", "value": { "typeParams": null, - "typeName": "f" + "typeName": "f", + "resolution": { + "kind": "local" + } } }, "kind": "const" diff --git a/tests/specs/namespace_reexport_member.txt b/tests/specs/namespace_reexport_member.txt index 5ed91077..58edbbaa 100644 --- a/tests/specs/namespace_reexport_member.txt +++ b/tests/specs/namespace_reexport_member.txt @@ -106,7 +106,12 @@ namespace default "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Chat" + "typeName": "Chat", + "resolution": { + "kind": "import", + "specifier": "./chat.ts", + "name": "Chat" + } } }, "readonly": false, @@ -224,7 +229,12 @@ namespace default "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Chat" + "typeName": "Chat", + "resolution": { + "kind": "import", + "specifier": "./chat.ts", + "name": "Chat" + } } }, "readonly": false, diff --git a/tests/specs/private_type_implementation_signature.txt b/tests/specs/private_type_implementation_signature.txt index d4120af7..c4e19031 100644 --- a/tests/specs/private_type_implementation_signature.txt +++ b/tests/specs/private_type_implementation_signature.txt @@ -116,7 +116,10 @@ private interface YesDiagnostic2 "kind": "typeRef", "value": { "typeParams": null, - "typeName": "YesDiagnostic1" + "typeName": "YesDiagnostic1", + "resolution": { + "kind": "local" + } } }, "hasBody": true, @@ -149,7 +152,10 @@ private interface YesDiagnostic2 "kind": "typeRef", "value": { "typeParams": null, - "typeName": "YesDiagnostic2" + "typeName": "YesDiagnostic2", + "resolution": { + "kind": "local" + } } }, "isAsync": false, @@ -173,7 +179,10 @@ private interface YesDiagnostic2 "kind": "typeRef", "value": { "typeParams": null, - "typeName": "NoDiagnostic" + "typeName": "NoDiagnostic", + "resolution": { + "kind": "local" + } } }, "hasBody": true, diff --git a/tests/specs/private_type_in_namespace.txt b/tests/specs/private_type_in_namespace.txt index 7c7faadb..f30a8611 100644 --- a/tests/specs/private_type_in_namespace.txt +++ b/tests/specs/private_type_in_namespace.txt @@ -100,7 +100,10 @@ private namespace Test "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Test.Other" + "typeName": "Test.Other", + "resolution": { + "kind": "local" + } } }, "typeParams": [] diff --git a/tests/specs/private_type_multiple_refs.txt b/tests/specs/private_type_multiple_refs.txt index 41896d2b..89a4bc3b 100644 --- a/tests/specs/private_type_multiple_refs.txt +++ b/tests/specs/private_type_multiple_refs.txt @@ -102,7 +102,12 @@ class ClassB "kind": "typeRef", "value": { "typeParams": null, - "typeName": "PrivateInterface" + "typeName": "PrivateInterface", + "resolution": { + "kind": "import", + "specifier": "./private.ts", + "name": "PrivateInterface" + } } }, "readonly": false, @@ -157,7 +162,12 @@ class ClassB "kind": "typeRef", "value": { "typeParams": null, - "typeName": "PrivateInterface" + "typeName": "PrivateInterface", + "resolution": { + "kind": "import", + "specifier": "./private.ts", + "name": "PrivateInterface" + } } }, "readonly": false, diff --git a/tests/specs/private_type_other_module.txt b/tests/specs/private_type_other_module.txt index e499e835..ec6ce0e1 100644 --- a/tests/specs/private_type_other_module.txt +++ b/tests/specs/private_type_other_module.txt @@ -124,7 +124,12 @@ class MyClass extends YesDiagnostic2 "kind": "typeRef", "value": { "typeParams": null, - "typeName": "MyNamespace.YesDiagnostic" + "typeName": "MyNamespace.YesDiagnostic", + "resolution": { + "kind": "import", + "specifier": "./diagnostic.ts", + "name": "MyNamespace" + } } }, "readonly": false, diff --git a/tests/specs/private_type_private_member.txt b/tests/specs/private_type_private_member.txt index 9b7e6566..9b984c2b 100644 --- a/tests/specs/private_type_private_member.txt +++ b/tests/specs/private_type_private_member.txt @@ -93,7 +93,10 @@ private interface YesDiagnostic "kind": "typeRef", "value": { "typeParams": null, - "typeName": "YesDiagnostic" + "typeName": "YesDiagnostic", + "resolution": { + "kind": "local" + } } }, "hasBody": true, @@ -122,7 +125,10 @@ private interface YesDiagnostic "kind": "typeRef", "value": { "typeParams": null, - "typeName": "NoDiagnostic" + "typeName": "NoDiagnostic", + "resolution": { + "kind": "local" + } } }, "hasBody": true, diff --git a/tests/specs/private_type_re_export_referencing.txt b/tests/specs/private_type_re_export_referencing.txt index 677081b4..12c0e698 100644 --- a/tests/specs/private_type_re_export_referencing.txt +++ b/tests/specs/private_type_re_export_referencing.txt @@ -98,7 +98,10 @@ class Data "kind": "typeRef", "value": { "typeParams": null, - "typeName": "PrivateNotExported" + "typeName": "PrivateNotExported", + "resolution": { + "kind": "local" + } } }, "readonly": false, @@ -123,7 +126,10 @@ class Data "kind": "typeRef", "value": { "typeParams": null, - "typeName": "PrivateExportedButNotFromModTs" + "typeName": "PrivateExportedButNotFromModTs", + "resolution": { + "kind": "local" + } } }, "readonly": false, @@ -152,7 +158,10 @@ class Data "kind": "typeRef", "value": { "typeParams": null, - "typeName": "PrivateReferencedInInternal" + "typeName": "PrivateReferencedInInternal", + "resolution": { + "kind": "local" + } } }, "readonly": false, diff --git a/tests/specs/private_type_referenced_from_class_member.txt b/tests/specs/private_type_referenced_from_class_member.txt index 6817d441..9c43826b 100644 --- a/tests/specs/private_type_referenced_from_class_member.txt +++ b/tests/specs/private_type_referenced_from_class_member.txt @@ -68,7 +68,10 @@ private class YesDiagnostic "kind": "typeRef", "value": { "typeParams": null, - "typeName": "YesDiagnostic" + "typeName": "YesDiagnostic", + "resolution": { + "kind": "local" + } } }, "readonly": false, diff --git a/tests/specs/re_export_clashes_private_type_bug.txt b/tests/specs/re_export_clashes_private_type_bug.txt index 57d795a4..ea463fa1 100644 --- a/tests/specs/re_export_clashes_private_type_bug.txt +++ b/tests/specs/re_export_clashes_private_type_bug.txt @@ -168,7 +168,10 @@ class MyClass "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Internal1" + "typeName": "Internal1", + "resolution": { + "kind": "local" + } } }, "readonly": false, @@ -193,7 +196,10 @@ class MyClass "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Internal2" + "typeName": "Internal2", + "resolution": { + "kind": "local" + } } }, "readonly": false, diff --git a/tests/specs/reexport_deep.txt b/tests/specs/reexport_deep.txt index 160821d3..bd227328 100644 --- a/tests/specs/reexport_deep.txt +++ b/tests/specs/reexport_deep.txt @@ -84,7 +84,12 @@ type UrlMessageEntity = MessageEntity.TextLinkMessageEntity "kind": "typeRef", "value": { "typeParams": null, - "typeName": "MessageEntity.TextLinkMessageEntity" + "typeName": "MessageEntity.TextLinkMessageEntity", + "resolution": { + "kind": "import", + "specifier": "./a.ts", + "name": "MessageEntity" + } } }, "typeParams": [] diff --git a/tests/specs/remote_module_re_export_no_diagnostics.txt b/tests/specs/remote_module_re_export_no_diagnostics.txt index 60842230..7befa88a 100644 --- a/tests/specs/remote_module_re_export_no_diagnostics.txt +++ b/tests/specs/remote_module_re_export_no_diagnostics.txt @@ -97,7 +97,10 @@ class PublicType2 "kind": "typeRef", "value": { "typeParams": null, - "typeName": "NonExportedType" + "typeName": "NonExportedType", + "resolution": { + "kind": "local" + } } }, "readonly": false, @@ -169,7 +172,10 @@ class PublicType2 "kind": "typeRef", "value": { "typeParams": null, - "typeName": "NonExportedType2" + "typeName": "NonExportedType2", + "resolution": { + "kind": "local" + } } }, "readonly": false, diff --git a/tests/specs/ts_user_defined_type_guards.txt b/tests/specs/ts_user_defined_type_guards.txt index 700576a4..4f019d30 100644 --- a/tests/specs/ts_user_defined_type_guards.txt +++ b/tests/specs/ts_user_defined_type_guards.txt @@ -271,7 +271,12 @@ class C "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "assertIsDefined", + "declaringKind": "function" + } } } } @@ -295,7 +300,12 @@ class C "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "assertIsDefined", + "declaringKind": "function" + } } } ], diff --git a/tests/specs/type_alias_infer_type.txt b/tests/specs/type_alias_infer_type.txt index a58920d1..739052b7 100644 --- a/tests/specs/type_alias_infer_type.txt +++ b/tests/specs/type_alias_infer_type.txt @@ -40,7 +40,12 @@ type Flatten = T extends Array ? U : T "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "Flatten", + "declaringKind": "typeAlias" + } } }, "extendsType": { @@ -74,7 +79,12 @@ type Flatten = T extends Array ? U : T "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "Flatten", + "declaringKind": "typeAlias" + } } } } diff --git a/tests/specs/type_generic_alias.txt b/tests/specs/type_generic_alias.txt index 34655411..3785b8e9 100644 --- a/tests/specs/type_generic_alias.txt +++ b/tests/specs/type_generic_alias.txt @@ -36,7 +36,12 @@ type A = T "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "A", + "declaringKind": "typeAlias" + } } }, "typeParams": [ diff --git a/tests/specs/type_import_type.txt b/tests/specs/type_import_type.txt index 62d6bb92..db427f08 100644 --- a/tests/specs/type_import_type.txt +++ b/tests/specs/type_import_type.txt @@ -48,7 +48,12 @@ function adopt(p: import("./module.ts").Pet): void "kind": "typeRef", "value": { "typeParams": null, - "typeName": "T" + "typeName": "T", + "resolution": { + "kind": "typeParam", + "declaringName": "adopt", + "declaringKind": "function" + } } } ] diff --git a/tests/specs/type_literal_mapped_type.txt b/tests/specs/type_literal_mapped_type.txt index 4aa76e7e..a442207d 100644 --- a/tests/specs/type_literal_mapped_type.txt +++ b/tests/specs/type_literal_mapped_type.txt @@ -48,7 +48,12 @@ type T = readonly [P in keyof Type as NewType]: Type[P] "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Type" + "typeName": "Type", + "resolution": { + "kind": "typeParam", + "declaringName": "T", + "declaringKind": "typeAlias" + } } } } @@ -72,7 +77,12 @@ type T = readonly [P in keyof Type as NewType]: Type[P] "kind": "typeRef", "value": { "typeParams": null, - "typeName": "Type" + "typeName": "Type", + "resolution": { + "kind": "typeParam", + "declaringName": "T", + "declaringKind": "typeAlias" + } } }, "indexType": { @@ -80,7 +90,10 @@ type T = readonly [P in keyof Type as NewType]: Type[P] "kind": "typeRef", "value": { "typeParams": null, - "typeName": "P" + "typeName": "P", + "resolution": { + "kind": "typeParam" + } } } } From 77c231ee1b8cfeade97f1fdb88fd030f8e48ab8d Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Mon, 9 Mar 2026 22:57:17 +0100 Subject: [PATCH 2/2] fmt --- src/ts_type.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/ts_type.rs b/src/ts_type.rs index 949450a0..6e08ce30 100644 --- a/src/ts_type.rs +++ b/src/ts_type.rs @@ -788,8 +788,7 @@ fn find_type_param_origin( for item in program.body() { match item { ModuleItemRef::ModuleDecl(ModuleDecl::ExportDecl(export)) => { - if let Some(origin) = find_type_param_in_decl(&export.decl, target_id) - { + if let Some(origin) = find_type_param_in_decl(&export.decl, target_id) { return Some(origin); } } @@ -824,8 +823,7 @@ fn find_type_param_origin( } } DefaultDecl::TsInterfaceDecl(iface) => { - if let Some(origin) = - find_type_param_in_interface(iface, target_id) + if let Some(origin) = find_type_param_in_interface(iface, target_id) { return Some(origin); } @@ -859,15 +857,10 @@ fn find_type_param_in_decl( None } } - Decl::TsInterface(iface) => { - find_type_param_in_interface(iface, target_id) - } + Decl::TsInterface(iface) => find_type_param_in_interface(iface, target_id), Decl::TsTypeAlias(alias) => { if has_type_param(&alias.type_params, target_id) { - Some(( - alias.id.sym.to_string(), - TypeParamDeclaringKind::TypeAlias, - )) + Some((alias.id.sym.to_string(), TypeParamDeclaringKind::TypeAlias)) } else { None } @@ -883,7 +876,9 @@ fn find_type_param_in_decl( ModuleItem::Stmt(Stmt::Decl(decl)) => Some(decl), _ => None, }; - if let Some(origin) = inner_decl.and_then(|inner_decl| find_type_param_in_decl(inner_decl, target_id)) { + if let Some(origin) = inner_decl.and_then(|inner_decl| { + find_type_param_in_decl(inner_decl, target_id) + }) { return Some(origin); } } @@ -905,7 +900,9 @@ fn find_type_param_in_class( // Check method type params for member in &class.body { - if let ClassMember::Method(method) = member && has_type_param(&method.function.type_params, target_id) { + if let ClassMember::Method(method) = member + && has_type_param(&method.function.type_params, target_id) + { let method_name = simple_prop_name(&method.key); return Some((method_name, TypeParamDeclaringKind::Method)); } @@ -919,10 +916,7 @@ fn find_type_param_in_interface( target_id: &Id, ) -> Option<(String, TypeParamDeclaringKind)> { if has_type_param(&iface.type_params, target_id) { - return Some(( - iface.id.sym.to_string(), - TypeParamDeclaringKind::Interface, - )); + return Some((iface.id.sym.to_string(), TypeParamDeclaringKind::Interface)); } // Check method type params