Skip to content

Commit

Permalink
JSDoc type reference understands require with entity name (#34804)
Browse files Browse the repository at this point in the history
* resolve require with entity name postfix

For example, `require("x").c`. This is the value equivalent of
`import("x").a.b.c`, but the syntax tree is not as nicely designed for
this purpose.

Fixes #34802

* Add bug number to test

* Add optional chain test
  • Loading branch information
sandersn committed Oct 29, 2019
1 parent 787ff34 commit 7d77ecb
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 7 deletions.
17 changes: 10 additions & 7 deletions src/compiler/checker.ts
Expand Up @@ -10759,14 +10759,17 @@ namespace ts {
let typeType = valueType;
if (symbol.valueDeclaration) {
const decl = getRootDeclaration(symbol.valueDeclaration);
const isRequireAlias = isVariableDeclaration(decl)
&& decl.initializer
&& isCallExpression(decl.initializer)
&& isRequireCall(decl.initializer, /*requireStringLiteralLikeArgument*/ true)
&& valueType.symbol;
const isImportType = node.kind === SyntaxKind.ImportType;
let isRequireAlias = false;
if (isVariableDeclaration(decl) && decl.initializer) {
let expr = decl.initializer;
// skip past entity names, eg `require("x").a.b.c`
while (isPropertyAccessExpression(expr)) {
expr = expr.expression;
}
isRequireAlias = isCallExpression(expr) && isRequireCall(expr, /*requireStringLiteralLikeArgument*/ true) && !!valueType.symbol;
}
const isDelayedMergeClass = symbol !== valueType.symbol && getMergedSymbol(symbol) === valueType.symbol;
if (isRequireAlias || isImportType || isDelayedMergeClass) {
if (isRequireAlias || node.kind === SyntaxKind.ImportType || isDelayedMergeClass) {
typeType = getTypeReferenceType(node, valueType.symbol);
}
}
Expand Down
58 changes: 58 additions & 0 deletions tests/baselines/reference/jsdocTypeReferenceToImport.symbols
@@ -0,0 +1,58 @@
=== tests/cases/conformance/jsdoc/jsdocTypeReferenceToImport.js ===
// #34802

const C = require('./ex').C;
>C : Symbol(C, Decl(jsdocTypeReferenceToImport.js, 2, 5))
>require('./ex').C : Symbol(C, Decl(ex.d.ts, 0, 0))
>require : Symbol(require)
>'./ex' : Symbol("tests/cases/conformance/jsdoc/ex", Decl(ex.d.ts, 0, 0))
>C : Symbol(C, Decl(ex.d.ts, 0, 0))

const D = require('./ex')?.C;
>D : Symbol(D, Decl(jsdocTypeReferenceToImport.js, 3, 5))
>require('./ex')?.C : Symbol(C, Decl(ex.d.ts, 0, 0))
>require : Symbol(require)
>'./ex' : Symbol("tests/cases/conformance/jsdoc/ex", Decl(ex.d.ts, 0, 0))
>C : Symbol(C, Decl(ex.d.ts, 0, 0))

/** @type {C} c */
var c = new C()
>c : Symbol(c, Decl(jsdocTypeReferenceToImport.js, 5, 3))
>C : Symbol(C, Decl(jsdocTypeReferenceToImport.js, 2, 5))

c.start
>c.start : Symbol(C.start, Decl(ex.d.ts, 0, 16))
>c : Symbol(c, Decl(jsdocTypeReferenceToImport.js, 5, 3))
>start : Symbol(C.start, Decl(ex.d.ts, 0, 16))

c.end
>c.end : Symbol(C.end, Decl(ex.d.ts, 1, 17))
>c : Symbol(c, Decl(jsdocTypeReferenceToImport.js, 5, 3))
>end : Symbol(C.end, Decl(ex.d.ts, 1, 17))

/** @type {D} c */
var d = new D()
>d : Symbol(d, Decl(jsdocTypeReferenceToImport.js, 10, 3))
>D : Symbol(D, Decl(jsdocTypeReferenceToImport.js, 3, 5))

d.start
>d.start : Symbol(C.start, Decl(ex.d.ts, 0, 16))
>d : Symbol(d, Decl(jsdocTypeReferenceToImport.js, 10, 3))
>start : Symbol(C.start, Decl(ex.d.ts, 0, 16))

d.end
>d.end : Symbol(C.end, Decl(ex.d.ts, 1, 17))
>d : Symbol(d, Decl(jsdocTypeReferenceToImport.js, 10, 3))
>end : Symbol(C.end, Decl(ex.d.ts, 1, 17))

=== tests/cases/conformance/jsdoc/ex.d.ts ===
export class C {
>C : Symbol(C, Decl(ex.d.ts, 0, 0))

start: number
>start : Symbol(C.start, Decl(ex.d.ts, 0, 16))

end: number
>end : Symbol(C.end, Decl(ex.d.ts, 1, 17))
}

62 changes: 62 additions & 0 deletions tests/baselines/reference/jsdocTypeReferenceToImport.types
@@ -0,0 +1,62 @@
=== tests/cases/conformance/jsdoc/jsdocTypeReferenceToImport.js ===
// #34802

const C = require('./ex').C;
>C : typeof import("tests/cases/conformance/jsdoc/ex").C
>require('./ex').C : typeof import("tests/cases/conformance/jsdoc/ex").C
>require('./ex') : typeof import("tests/cases/conformance/jsdoc/ex")
>require : any
>'./ex' : "./ex"
>C : typeof import("tests/cases/conformance/jsdoc/ex").C

const D = require('./ex')?.C;
>D : typeof import("tests/cases/conformance/jsdoc/ex").C
>require('./ex')?.C : typeof import("tests/cases/conformance/jsdoc/ex").C
>require('./ex') : typeof import("tests/cases/conformance/jsdoc/ex")
>require : any
>'./ex' : "./ex"
>C : typeof import("tests/cases/conformance/jsdoc/ex").C

/** @type {C} c */
var c = new C()
>c : import("tests/cases/conformance/jsdoc/ex").C
>new C() : import("tests/cases/conformance/jsdoc/ex").C
>C : typeof import("tests/cases/conformance/jsdoc/ex").C

c.start
>c.start : number
>c : import("tests/cases/conformance/jsdoc/ex").C
>start : number

c.end
>c.end : number
>c : import("tests/cases/conformance/jsdoc/ex").C
>end : number

/** @type {D} c */
var d = new D()
>d : import("tests/cases/conformance/jsdoc/ex").C
>new D() : import("tests/cases/conformance/jsdoc/ex").C
>D : typeof import("tests/cases/conformance/jsdoc/ex").C

d.start
>d.start : number
>d : import("tests/cases/conformance/jsdoc/ex").C
>start : number

d.end
>d.end : number
>d : import("tests/cases/conformance/jsdoc/ex").C
>end : number

=== tests/cases/conformance/jsdoc/ex.d.ts ===
export class C {
>C : C

start: number
>start : number

end: number
>end : number
}

23 changes: 23 additions & 0 deletions tests/cases/conformance/jsdoc/jsdocTypeReferenceToImport.ts
@@ -0,0 +1,23 @@
// #34802
// @Filename: jsdocTypeReferenceToImport.js
// @noEmit: true
// @allowJs: true
// @checkJs: true

const C = require('./ex').C;
const D = require('./ex')?.C;
/** @type {C} c */
var c = new C()
c.start
c.end

/** @type {D} c */
var d = new D()
d.start
d.end

// @Filename: ex.d.ts
export class C {
start: number
end: number
}

0 comments on commit 7d77ecb

Please sign in to comment.