diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 10ddb3c0c88b4..b4975ec3a77be 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -3078,10 +3078,26 @@ func (c *Checker) checkTypeReferenceOrImport(node *ast.Node) { c.checkTypeArgumentConstraints(node, typeParameters) } } - symbol := c.getResolvedSymbolOrNil(node) - if symbol != nil { - if core.Some(symbol.Declarations(), func(d *ast.Node) bool { return ast.IsTypeDeclaration(d) && c.IsDeprecatedDeclaration(d) }) { - c.addDeprecatedSuggestion(c.getDeprecatedSuggestionNode(node), symbol.Declarations(), symbol.Name()) + name := getTypeReferenceName(node) + var aliasSymbol *ast.Symbol + if name != nil { + aliasSymbol = c.resolveEntityName(name, ast.SymbolFlagsType, true /*ignoreErrors*/, true /*dontResolveAlias*/, nil /*location*/) + } else if ast.IsImportTypeNode(node) && !ast.NodeIsMissing(node.AsImportTypeNode().Qualifier) { + aliasSymbol = c.getResolvedSymbolOrNil(node.AsImportTypeNode().Qualifier) + } + if aliasSymbol != nil && aliasSymbol.Flags()&ast.SymbolFlagsAlias != 0 && aliasSymbol.Flags()&ast.SymbolFlagsType == 0 { + targetSymbol := c.resolveAliasWithDeprecationCheck(aliasSymbol, c.getDeprecatedSuggestionNode(node)) + if targetSymbol != nil && core.Some(targetSymbol.Declarations(), func(d *ast.Node) bool { + return (ast.IsTypeDeclaration(d) || d.Kind == ast.KindImportEqualsDeclaration || d.Kind == ast.KindImportSpecifier || d.Kind == ast.KindExportSpecifier) && c.IsDeprecatedDeclaration(d) + }) { + c.addDeprecatedSuggestion(c.getDeprecatedSuggestionNode(node), targetSymbol.Declarations(), targetSymbol.Name()) + } + } else { + symbol := c.getResolvedSymbolOrNil(node) + if symbol != nil { + if core.Some(symbol.Declarations(), func(d *ast.Node) bool { return ast.IsTypeDeclaration(d) && c.IsDeprecatedDeclaration(d) }) { + c.addDeprecatedSuggestion(c.getDeprecatedSuggestionNode(node), symbol.Declarations(), symbol.Name()) + } } } } @@ -3122,6 +3138,14 @@ func (c *Checker) getDeprecatedSuggestionNode(node *ast.Node) *ast.Node { if ast.IsQualifiedName(typeName) { return typeName.AsQualifiedName().Right } + case ast.KindImportType: + qualifier := node.AsImportTypeNode().Qualifier + if qualifier != nil { + if ast.IsQualifiedName(qualifier) { + return qualifier.AsQualifiedName().Right + } + return qualifier + } } return node } diff --git a/tsc/internal/checker/checker_test.go b/tsc/internal/checker/checker_test.go index 31cea19751525..429ee39b1aa7c 100644 --- a/tsc/internal/checker/checker_test.go +++ b/tsc/internal/checker/checker_test.go @@ -208,3 +208,61 @@ export type Initial = Box<"first">; assert.Equal(t, c.TypeToString(initial), "Initial") assert.Equal(t, len(c.GetDiagnostics(t.Context(), file)), 0) } + +func TestDeprecatedNamespaceAliasInTypeReference(t *testing.T) { + t.Parallel() + + fs := bundled.WrapFS(vfstest.FromMap(map[string]string{ + "/example.d.ts": `export type Bar = string | number; +/** @deprecated */ +export type Baz = string | number; +export { /** @deprecated */ Bar as Old } from './example';`, + "/values.d.ts": `export const val = 1;`, + "/foo.ts": `import * as example from './example'; + +export { val } from './values'; +/** @deprecated */ +export type val = number; + +export namespace JSX { + /** @deprecated */ + export type Foo = string | number; + + /** @deprecated */ + export import Bar = example.Bar; + + export import Baz = example.Baz; + + export import Old = example.Old; +} + +let x: JSX.Foo; +let y: JSX.Bar; +let z: JSX.Baz; +let o: JSX.Old; +let m: val; +let w: import('./foo').JSX.Bar;`, + }, tspath.CaseSensitive)) + opts := &core.CompilerOptions{NoLib: core.TSTrue} + program := compiler.NewProgram(compiler.ProgramOptions{ + Config: tsoptions.NewParsedCommandLine(opts, []tspath.RootedFilePath{"/example.d.ts", "/values.d.ts", "/foo.ts"}, nil, "/", fs.CaseSensitivity()), + Host: compiler.NewCompilerHost(fs, bundled.LibPath(), nil, nil, nil), + }) + program.BindSourceFiles() + c, done := program.GetTypeChecker(t.Context()) + defer done() + file := program.GetSourceFile("/foo.ts") + var deprecatedSuggestions []*ast.Diagnostic + for _, s := range c.GetSuggestionDiagnostics(t.Context(), file) { + if s.ReportsDeprecated() { + deprecatedSuggestions = append(deprecatedSuggestions, s) + } + } + assert.Equal(t, len(deprecatedSuggestions), 6) + assert.Equal(t, deprecatedSuggestions[0].MessageArgs()[0], "Foo") + assert.Equal(t, deprecatedSuggestions[1].MessageArgs()[0], "Bar") + assert.Equal(t, deprecatedSuggestions[2].MessageArgs()[0], "Baz") + assert.Equal(t, deprecatedSuggestions[3].MessageArgs()[0], "Old") + assert.Equal(t, deprecatedSuggestions[4].MessageArgs()[0], "val") + assert.Equal(t, deprecatedSuggestions[5].MessageArgs()[0], "Bar") +}