Improve import type support for commonjs exports (#49745)

* Improve import type support for commonjs exports

This PR makes getTypeFromImportTypeNode a little more like
getExternalModuleMember: for JS files, it now uses both
`getTypeOfSymbol` and `getExportsOfSymbol`, and uses whichever one
returns a symbol. This allows using arbitrary properties of a CJS export=
as types in JSDoc; previously a special case in the binder enabled only
CJS export= where all properties were shorthand assignments.

Fixes #49195

* Add js types/value test case

* Improve binding of CJS property assignments

1. Bind property assignments same as shorthand property assignments in
module.exports object literal assignments.
2. Bind all such assignments, even if the object literal contains
non-property assignments. This is different from before, and it requires
slightly smarter code to prefer aliases when checking CJS imports.

* Remove new binder code

Just include the original fix

* revert missed type in binder
This commit is contained in:
Nathan Shively-Sanders
2022-08-01 10:57:38 -07:00
committed by GitHub
parent e1ceb2eb81
commit 427d43691a
5 changed files with 612 additions and 9 deletions

View File

@@ -3099,11 +3099,6 @@ namespace ts {
return getNodeLinks(expression).resolvedSymbol;
}
function getTargetOfPropertyAssignment(node: PropertyAssignment, dontRecursivelyResolve: boolean): Symbol | undefined {
const expression = node.initializer;
return getTargetOfAliasLikeExpression(expression, dontRecursivelyResolve);
}
function getTargetOfAccessExpression(node: AccessExpression, dontRecursivelyResolve: boolean): Symbol | undefined {
if (!(isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.EqualsToken)) {
return undefined;
@@ -3136,7 +3131,7 @@ namespace ts {
case SyntaxKind.ShorthandPropertyAssignment:
return resolveEntityName((node as ShorthandPropertyAssignment).name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ true, dontRecursivelyResolve);
case SyntaxKind.PropertyAssignment:
return getTargetOfPropertyAssignment(node as PropertyAssignment, dontRecursivelyResolve);
return getTargetOfAliasLikeExpression((node as PropertyAssignment).initializer, dontRecursivelyResolve);
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.PropertyAccessExpression:
return getTargetOfAccessExpression(node as AccessExpression, dontRecursivelyResolve);
@@ -9353,7 +9348,7 @@ namespace ts {
}
(resolvedSymbol || symbol).exports!.forEach((s, name) => {
const exportedMember = members.get(name)!;
if (exportedMember && exportedMember !== s) {
if (exportedMember && exportedMember !== s && !(s.flags & SymbolFlags.Alias)) {
if (s.flags & SymbolFlags.Value && exportedMember.flags & SymbolFlags.Value) {
// If the member has an additional value-like declaration, union the types from the two declarations,
// but issue an error if they occurred in two different files. The purpose is to support a JS file with
@@ -16298,6 +16293,7 @@ namespace ts {
links.resolvedSymbol = unknownSymbol;
return links.resolvedType = errorType;
}
const isExportEquals = !!innerModuleSymbol.exports?.get(InternalSymbolName.ExportEquals);
const moduleSymbol = resolveExternalModuleSymbol(innerModuleSymbol, /*dontResolveAlias*/ false);
if (!nodeIsMissing(node.qualifier)) {
const nameStack: Identifier[] = getIdentifierChain(node.qualifier!);
@@ -16309,9 +16305,11 @@ namespace ts {
// That, in turn, ultimately uses `getPropertyOfType` on the type of the symbol, which differs slightly from
// the `exports` lookup process that only looks up namespace members which is used for most type references
const mergedResolvedSymbol = getMergedSymbol(resolveSymbol(currentNamespace));
const next = node.isTypeOf
const symbolFromVariable = node.isTypeOf || isInJSFile(node) && isExportEquals
? getPropertyOfType(getTypeOfSymbol(mergedResolvedSymbol), current.escapedText, /*skipObjectFunctionPropertyAugment*/ false, /*includeTypeOnlyMembers*/ true)
: getSymbol(getExportsOfSymbol(mergedResolvedSymbol), current.escapedText, meaning);
: undefined;
const symbolFromModule = node.isTypeOf ? undefined : getSymbol(getExportsOfSymbol(mergedResolvedSymbol), current.escapedText, meaning);
const next = symbolFromModule ?? symbolFromVariable;
if (!next) {
error(current, Diagnostics.Namespace_0_has_no_exported_member_1, getFullyQualifiedName(currentNamespace), declarationNameToString(current));
return links.resolvedType = errorType;

View File

@@ -0,0 +1,92 @@
tests/cases/conformance/salsa/index.ts(2,24): error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'Thing'.
tests/cases/conformance/salsa/index.ts(3,24): error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'AnotherThing'.
tests/cases/conformance/salsa/index.ts(4,24): error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'foo'.
tests/cases/conformance/salsa/index.ts(5,24): error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'qux'.
tests/cases/conformance/salsa/index.ts(6,24): error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'baz'.
tests/cases/conformance/salsa/index.ts(8,24): error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'literal'.
tests/cases/conformance/salsa/index.ts(19,31): error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'buz'.
tests/cases/conformance/salsa/main.js(20,35): error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'buz'.
==== tests/cases/conformance/salsa/mod.js (0 errors) ====
class Thing { x = 1 }
class AnotherThing { y = 2 }
function foo() { return 3 }
function bar() { return 4 }
/** @typedef {() => number} buz */
module.exports = {
Thing,
AnotherThing,
foo,
qux: bar,
baz() { return 5 },
literal: "",
}
==== tests/cases/conformance/salsa/main.js (1 errors) ====
/**
* @param {import("./mod").Thing} a
* @param {import("./mod").AnotherThing} b
* @param {import("./mod").foo} c
* @param {import("./mod").qux} d
* @param {import("./mod").baz} e
* @param {import("./mod").buz} f
* @param {import("./mod").literal} g
*/
function jstypes(a, b, c, d, e, f, g) {
return a.x + b.y + c() + d() + e() + f() + g.length
}
/**
* @param {typeof import("./mod").Thing} a
* @param {typeof import("./mod").AnotherThing} b
* @param {typeof import("./mod").foo} c
* @param {typeof import("./mod").qux} d
* @param {typeof import("./mod").baz} e
* @param {typeof import("./mod").buz} f
~~~
!!! error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'buz'.
* @param {typeof import("./mod").literal} g
*/
function jsvalues(a, b, c, d, e, f, g) {
return a.length + b.length + c() + d() + e() + f() + g.length
}
==== tests/cases/conformance/salsa/index.ts (7 errors) ====
function types(
a: import('./mod').Thing,
~~~~~
!!! error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'Thing'.
b: import('./mod').AnotherThing,
~~~~~~~~~~~~
!!! error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'AnotherThing'.
c: import('./mod').foo,
~~~
!!! error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'foo'.
d: import('./mod').qux,
~~~
!!! error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'qux'.
e: import('./mod').baz,
~~~
!!! error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'baz'.
f: import('./mod').buz,
g: import('./mod').literal,
~~~~~~~
!!! error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'literal'.
) {
return a.x + b.y + c() + d() + e() + f() + g.length
}
function values(
a: typeof import('./mod').Thing,
b: typeof import('./mod').AnotherThing,
c: typeof import('./mod').foo,
d: typeof import('./mod').qux,
e: typeof import('./mod').baz,
f: typeof import('./mod').buz,
~~~
!!! error TS2694: Namespace '"tests/cases/conformance/salsa/mod".export=' has no exported member 'buz'.
g: typeof import('./mod').literal,
) {
return a.length + b.length + c() + d() + e() + f() + g.length
}

View File

@@ -0,0 +1,195 @@
=== tests/cases/conformance/salsa/mod.js ===
class Thing { x = 1 }
>Thing : Symbol(Thing, Decl(mod.js, 0, 0))
>x : Symbol(Thing.x, Decl(mod.js, 0, 14))
class AnotherThing { y = 2 }
>AnotherThing : Symbol(AnotherThing, Decl(mod.js, 0, 22))
>y : Symbol(AnotherThing.y, Decl(mod.js, 1, 20))
function foo() { return 3 }
>foo : Symbol(foo, Decl(mod.js, 1, 29))
function bar() { return 4 }
>bar : Symbol(bar, Decl(mod.js, 2, 27))
/** @typedef {() => number} buz */
module.exports = {
>module.exports : Symbol(module.exports, Decl(mod.js, 0, 0))
>module : Symbol(export=, Decl(mod.js, 3, 27))
>exports : Symbol(export=, Decl(mod.js, 3, 27))
Thing,
>Thing : Symbol(Thing, Decl(mod.js, 5, 18))
AnotherThing,
>AnotherThing : Symbol(AnotherThing, Decl(mod.js, 6, 10))
foo,
>foo : Symbol(foo, Decl(mod.js, 7, 17))
qux: bar,
>qux : Symbol(qux, Decl(mod.js, 8, 8))
>bar : Symbol(bar, Decl(mod.js, 2, 27))
baz() { return 5 },
>baz : Symbol(baz, Decl(mod.js, 9, 13))
literal: "",
>literal : Symbol(literal, Decl(mod.js, 10, 23))
}
=== tests/cases/conformance/salsa/main.js ===
/**
* @param {import("./mod").Thing} a
* @param {import("./mod").AnotherThing} b
* @param {import("./mod").foo} c
* @param {import("./mod").qux} d
* @param {import("./mod").baz} e
* @param {import("./mod").buz} f
* @param {import("./mod").literal} g
*/
function jstypes(a, b, c, d, e, f, g) {
>jstypes : Symbol(jstypes, Decl(main.js, 0, 0))
>a : Symbol(a, Decl(main.js, 9, 17))
>b : Symbol(b, Decl(main.js, 9, 19))
>c : Symbol(c, Decl(main.js, 9, 22))
>d : Symbol(d, Decl(main.js, 9, 25))
>e : Symbol(e, Decl(main.js, 9, 28))
>f : Symbol(f, Decl(main.js, 9, 31))
>g : Symbol(g, Decl(main.js, 9, 34))
return a.x + b.y + c() + d() + e() + f() + g.length
>a.x : Symbol(Thing.x, Decl(mod.js, 0, 14))
>a : Symbol(a, Decl(main.js, 9, 17))
>x : Symbol(Thing.x, Decl(mod.js, 0, 14))
>b.y : Symbol(AnotherThing.y, Decl(mod.js, 1, 20))
>b : Symbol(b, Decl(main.js, 9, 19))
>y : Symbol(AnotherThing.y, Decl(mod.js, 1, 20))
>c : Symbol(c, Decl(main.js, 9, 22))
>d : Symbol(d, Decl(main.js, 9, 25))
>e : Symbol(e, Decl(main.js, 9, 28))
>f : Symbol(f, Decl(main.js, 9, 31))
>g.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>g : Symbol(g, Decl(main.js, 9, 34))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
}
/**
* @param {typeof import("./mod").Thing} a
* @param {typeof import("./mod").AnotherThing} b
* @param {typeof import("./mod").foo} c
* @param {typeof import("./mod").qux} d
* @param {typeof import("./mod").baz} e
* @param {typeof import("./mod").buz} f
* @param {typeof import("./mod").literal} g
*/
function jsvalues(a, b, c, d, e, f, g) {
>jsvalues : Symbol(jsvalues, Decl(main.js, 11, 1))
>a : Symbol(a, Decl(main.js, 22, 18))
>b : Symbol(b, Decl(main.js, 22, 20))
>c : Symbol(c, Decl(main.js, 22, 23))
>d : Symbol(d, Decl(main.js, 22, 26))
>e : Symbol(e, Decl(main.js, 22, 29))
>f : Symbol(f, Decl(main.js, 22, 32))
>g : Symbol(g, Decl(main.js, 22, 35))
return a.length + b.length + c() + d() + e() + f() + g.length
>a.length : Symbol(Function.length, Decl(lib.es5.d.ts, --, --))
>a : Symbol(a, Decl(main.js, 22, 18))
>length : Symbol(Function.length, Decl(lib.es5.d.ts, --, --))
>b.length : Symbol(Function.length, Decl(lib.es5.d.ts, --, --))
>b : Symbol(b, Decl(main.js, 22, 20))
>length : Symbol(Function.length, Decl(lib.es5.d.ts, --, --))
>c : Symbol(c, Decl(main.js, 22, 23))
>d : Symbol(d, Decl(main.js, 22, 26))
>e : Symbol(e, Decl(main.js, 22, 29))
>f : Symbol(f, Decl(main.js, 22, 32))
>g.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>g : Symbol(g, Decl(main.js, 22, 35))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
}
=== tests/cases/conformance/salsa/index.ts ===
function types(
>types : Symbol(types, Decl(index.ts, 0, 0))
a: import('./mod').Thing,
>a : Symbol(a, Decl(index.ts, 0, 15))
b: import('./mod').AnotherThing,
>b : Symbol(b, Decl(index.ts, 1, 29))
c: import('./mod').foo,
>c : Symbol(c, Decl(index.ts, 2, 36))
d: import('./mod').qux,
>d : Symbol(d, Decl(index.ts, 3, 27))
e: import('./mod').baz,
>e : Symbol(e, Decl(index.ts, 4, 27))
f: import('./mod').buz,
>f : Symbol(f, Decl(index.ts, 5, 27))
>buz : Symbol(buz, Decl(mod.js, 4, 4))
g: import('./mod').literal,
>g : Symbol(g, Decl(index.ts, 6, 27))
) {
return a.x + b.y + c() + d() + e() + f() + g.length
>a : Symbol(a, Decl(index.ts, 0, 15))
>b : Symbol(b, Decl(index.ts, 1, 29))
>c : Symbol(c, Decl(index.ts, 2, 36))
>d : Symbol(d, Decl(index.ts, 3, 27))
>e : Symbol(e, Decl(index.ts, 4, 27))
>f : Symbol(f, Decl(index.ts, 5, 27))
>g : Symbol(g, Decl(index.ts, 6, 27))
}
function values(
>values : Symbol(values, Decl(index.ts, 10, 1))
a: typeof import('./mod').Thing,
>a : Symbol(a, Decl(index.ts, 12, 16))
>Thing : Symbol(Thing, Decl(mod.js, 5, 18))
b: typeof import('./mod').AnotherThing,
>b : Symbol(b, Decl(index.ts, 13, 36))
>AnotherThing : Symbol(AnotherThing, Decl(mod.js, 6, 10))
c: typeof import('./mod').foo,
>c : Symbol(c, Decl(index.ts, 14, 43))
>foo : Symbol(foo, Decl(mod.js, 7, 17))
d: typeof import('./mod').qux,
>d : Symbol(d, Decl(index.ts, 15, 34))
>qux : Symbol(qux, Decl(mod.js, 8, 8))
e: typeof import('./mod').baz,
>e : Symbol(e, Decl(index.ts, 16, 34))
>baz : Symbol(baz, Decl(mod.js, 9, 13))
f: typeof import('./mod').buz,
>f : Symbol(f, Decl(index.ts, 17, 34))
g: typeof import('./mod').literal,
>g : Symbol(g, Decl(index.ts, 18, 34))
>literal : Symbol(literal, Decl(mod.js, 10, 23))
) {
return a.length + b.length + c() + d() + e() + f() + g.length
>a.length : Symbol(Function.length, Decl(lib.es5.d.ts, --, --))
>a : Symbol(a, Decl(index.ts, 12, 16))
>length : Symbol(Function.length, Decl(lib.es5.d.ts, --, --))
>b.length : Symbol(Function.length, Decl(lib.es5.d.ts, --, --))
>b : Symbol(b, Decl(index.ts, 13, 36))
>length : Symbol(Function.length, Decl(lib.es5.d.ts, --, --))
>c : Symbol(c, Decl(index.ts, 14, 43))
>d : Symbol(d, Decl(index.ts, 15, 34))
>e : Symbol(e, Decl(index.ts, 16, 34))
>f : Symbol(f, Decl(index.ts, 17, 34))
>g.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>g : Symbol(g, Decl(index.ts, 18, 34))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
}

View File

@@ -0,0 +1,249 @@
=== tests/cases/conformance/salsa/mod.js ===
class Thing { x = 1 }
>Thing : Thing
>x : number
>1 : 1
class AnotherThing { y = 2 }
>AnotherThing : AnotherThing
>y : number
>2 : 2
function foo() { return 3 }
>foo : () => number
>3 : 3
function bar() { return 4 }
>bar : () => number
>4 : 4
/** @typedef {() => number} buz */
module.exports = {
>module.exports = { Thing, AnotherThing, foo, qux: bar, baz() { return 5 }, literal: "",} : { Thing: typeof Thing; AnotherThing: typeof AnotherThing; foo: () => number; qux: () => number; baz(): number; literal: string; }
>module.exports : { Thing: typeof Thing; AnotherThing: typeof AnotherThing; foo: () => number; qux: () => number; baz(): number; literal: string; }
>module : { exports: { Thing: typeof Thing; AnotherThing: typeof AnotherThing; foo: () => number; qux: () => number; baz(): number; literal: string; }; }
>exports : { Thing: typeof Thing; AnotherThing: typeof AnotherThing; foo: () => number; qux: () => number; baz(): number; literal: string; }
>{ Thing, AnotherThing, foo, qux: bar, baz() { return 5 }, literal: "",} : { Thing: typeof Thing; AnotherThing: typeof AnotherThing; foo: () => number; qux: () => number; baz(): number; literal: string; }
Thing,
>Thing : typeof Thing
AnotherThing,
>AnotherThing : typeof AnotherThing
foo,
>foo : () => number
qux: bar,
>qux : () => number
>bar : () => number
baz() { return 5 },
>baz : () => number
>5 : 5
literal: "",
>literal : string
>"" : ""
}
=== tests/cases/conformance/salsa/main.js ===
/**
* @param {import("./mod").Thing} a
* @param {import("./mod").AnotherThing} b
* @param {import("./mod").foo} c
* @param {import("./mod").qux} d
* @param {import("./mod").baz} e
* @param {import("./mod").buz} f
* @param {import("./mod").literal} g
*/
function jstypes(a, b, c, d, e, f, g) {
>jstypes : (a: Thing, b: AnotherThing, c: () => number, d: () => number, e: () => number, f: import("./mod").buz, g: string) => number
>a : Thing
>b : AnotherThing
>c : () => number
>d : () => number
>e : () => number
>f : import("tests/cases/conformance/salsa/mod").buz
>g : string
return a.x + b.y + c() + d() + e() + f() + g.length
>a.x + b.y + c() + d() + e() + f() + g.length : number
>a.x + b.y + c() + d() + e() + f() : number
>a.x + b.y + c() + d() + e() : number
>a.x + b.y + c() + d() : number
>a.x + b.y + c() : number
>a.x + b.y : number
>a.x : number
>a : Thing
>x : number
>b.y : number
>b : AnotherThing
>y : number
>c() : number
>c : () => number
>d() : number
>d : () => number
>e() : number
>e : () => number
>f() : number
>f : import("tests/cases/conformance/salsa/mod").buz
>g.length : number
>g : string
>length : number
}
/**
* @param {typeof import("./mod").Thing} a
* @param {typeof import("./mod").AnotherThing} b
* @param {typeof import("./mod").foo} c
* @param {typeof import("./mod").qux} d
* @param {typeof import("./mod").baz} e
* @param {typeof import("./mod").buz} f
* @param {typeof import("./mod").literal} g
*/
function jsvalues(a, b, c, d, e, f, g) {
>jsvalues : (a: typeof import("./mod").Thing, b: typeof import("./mod").AnotherThing, c: typeof import("./mod").foo, d: typeof import("./mod").qux, e: typeof import("./mod").baz, f: any, g: typeof import("./mod").literal) => any
>a : typeof Thing
>b : typeof AnotherThing
>c : () => number
>d : () => number
>e : () => number
>f : any
>g : string
return a.length + b.length + c() + d() + e() + f() + g.length
>a.length + b.length + c() + d() + e() + f() + g.length : any
>a.length + b.length + c() + d() + e() + f() : any
>a.length + b.length + c() + d() + e() : number
>a.length + b.length + c() + d() : number
>a.length + b.length + c() : number
>a.length + b.length : number
>a.length : number
>a : typeof Thing
>length : number
>b.length : number
>b : typeof AnotherThing
>length : number
>c() : number
>c : () => number
>d() : number
>d : () => number
>e() : number
>e : () => number
>f() : any
>f : any
>g.length : number
>g : string
>length : number
}
=== tests/cases/conformance/salsa/index.ts ===
function types(
>types : (a: any, b: any, c: any, d: any, e: any, f: import('./mod').buz, g: any) => any
a: import('./mod').Thing,
>a : any
b: import('./mod').AnotherThing,
>b : any
c: import('./mod').foo,
>c : any
d: import('./mod').qux,
>d : any
e: import('./mod').baz,
>e : any
f: import('./mod').buz,
>f : import("tests/cases/conformance/salsa/mod").buz
g: import('./mod').literal,
>g : any
) {
return a.x + b.y + c() + d() + e() + f() + g.length
>a.x + b.y + c() + d() + e() + f() + g.length : any
>a.x + b.y + c() + d() + e() + f() : any
>a.x + b.y + c() + d() + e() : any
>a.x + b.y + c() + d() : any
>a.x + b.y + c() : any
>a.x + b.y : any
>a.x : any
>a : any
>x : any
>b.y : any
>b : any
>y : any
>c() : any
>c : any
>d() : any
>d : any
>e() : any
>e : any
>f() : number
>f : import("tests/cases/conformance/salsa/mod").buz
>g.length : any
>g : any
>length : any
}
function values(
>values : (a: typeof import('./mod').Thing, b: typeof import('./mod').AnotherThing, c: typeof import('./mod').foo, d: typeof import('./mod').qux, e: typeof import('./mod').baz, f: any, g: typeof import('./mod').literal) => any
a: typeof import('./mod').Thing,
>a : typeof Thing
>Thing : any
b: typeof import('./mod').AnotherThing,
>b : typeof AnotherThing
>AnotherThing : any
c: typeof import('./mod').foo,
>c : () => number
>foo : any
d: typeof import('./mod').qux,
>d : () => number
>qux : any
e: typeof import('./mod').baz,
>e : () => number
>baz : any
f: typeof import('./mod').buz,
>f : any
>buz : any
g: typeof import('./mod').literal,
>g : string
>literal : any
) {
return a.length + b.length + c() + d() + e() + f() + g.length
>a.length + b.length + c() + d() + e() + f() + g.length : any
>a.length + b.length + c() + d() + e() + f() : any
>a.length + b.length + c() + d() + e() : number
>a.length + b.length + c() + d() : number
>a.length + b.length + c() : number
>a.length + b.length : number
>a.length : number
>a : typeof Thing
>length : number
>b.length : number
>b : typeof AnotherThing
>length : number
>c() : number
>c : () => number
>d() : number
>d : () => number
>e() : number
>e : () => number
>f() : any
>f : any
>g.length : number
>g : string
>length : number
}

View File

@@ -0,0 +1,69 @@
// @noEmit: true
// @allowJs: true
// @checkJs: true
// @Filename: mod.js
class Thing { x = 1 }
class AnotherThing { y = 2 }
function foo() { return 3 }
function bar() { return 4 }
/** @typedef {() => number} buz */
module.exports = {
Thing,
AnotherThing,
foo,
qux: bar,
baz() { return 5 },
literal: "",
}
// @Filename: main.js
/**
* @param {import("./mod").Thing} a
* @param {import("./mod").AnotherThing} b
* @param {import("./mod").foo} c
* @param {import("./mod").qux} d
* @param {import("./mod").baz} e
* @param {import("./mod").buz} f
* @param {import("./mod").literal} g
*/
function jstypes(a, b, c, d, e, f, g) {
return a.x + b.y + c() + d() + e() + f() + g.length
}
/**
* @param {typeof import("./mod").Thing} a
* @param {typeof import("./mod").AnotherThing} b
* @param {typeof import("./mod").foo} c
* @param {typeof import("./mod").qux} d
* @param {typeof import("./mod").baz} e
* @param {typeof import("./mod").buz} f
* @param {typeof import("./mod").literal} g
*/
function jsvalues(a, b, c, d, e, f, g) {
return a.length + b.length + c() + d() + e() + f() + g.length
}
// @Filename: index.ts
function types(
a: import('./mod').Thing,
b: import('./mod').AnotherThing,
c: import('./mod').foo,
d: import('./mod').qux,
e: import('./mod').baz,
f: import('./mod').buz,
g: import('./mod').literal,
) {
return a.x + b.y + c() + d() + e() + f() + g.length
}
function values(
a: typeof import('./mod').Thing,
b: typeof import('./mod').AnotherThing,
c: typeof import('./mod').foo,
d: typeof import('./mod').qux,
e: typeof import('./mod').baz,
f: typeof import('./mod').buz,
g: typeof import('./mod').literal,
) {
return a.length + b.length + c() + d() + e() + f() + g.length
}