Skip to content

Commit 1cbe7ef

Browse files
authored
Fix crash in JS declaration emit (#38508)
* Fix crash in JS decl emit * Emit as class with private ctor
1 parent 7fc456f commit 1cbe7ef

File tree

6 files changed

+86
-2
lines changed

6 files changed

+86
-2
lines changed

src/compiler/checker.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5949,6 +5949,7 @@ namespace ts {
59495949
}
59505950
}
59515951

5952+
59525953
// Synthesize declarations for a symbol - might be an Interface, a Class, a Namespace, a Type, a Variable (const, let, or var), an Alias
59535954
// or a merge of some number of those.
59545955
// An interesting challenge is ensuring that when classes merge with namespaces and interfaces, is keeping
@@ -6317,7 +6318,10 @@ namespace ts {
63176318
const baseTypes = getBaseTypes(classType);
63186319
const implementsTypes = getImplementsTypes(classType);
63196320
const staticType = getTypeOfSymbol(symbol);
6320-
const staticBaseType = getBaseConstructorTypeOfClass(staticType as InterfaceType);
6321+
const isClass = !!staticType.symbol?.valueDeclaration && isClassLike(staticType.symbol.valueDeclaration);
6322+
const staticBaseType = isClass
6323+
? getBaseConstructorTypeOfClass(staticType as InterfaceType)
6324+
: anyType;
63216325
const heritageClauses = [
63226326
...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
63236327
...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
@@ -6353,7 +6357,17 @@ namespace ts {
63536357
const staticMembers = flatMap(
63546358
filter(getPropertiesOfType(staticType), p => !(p.flags & SymbolFlags.Prototype) && p.escapedName !== "prototype" && !isNamespaceMember(p)),
63556359
p => serializePropertySymbolForClass(p, /*isStatic*/ true, staticBaseType));
6356-
const constructors = serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
6360+
// When we encounter an `X.prototype.y` assignment in a JS file, we bind `X` as a class regardless as to whether
6361+
// the value is ever initialized with a class or function-like value. For cases where `X` could never be
6362+
// created via `new`, we will inject a `private constructor()` declaration to indicate it is not createable.
6363+
const isNonConstructableClassLikeInJsFile =
6364+
!isClass &&
6365+
!!symbol.valueDeclaration &&
6366+
isInJSFile(symbol.valueDeclaration) &&
6367+
!some(getSignaturesOfType(staticType, SignatureKind.Construct));
6368+
const constructors = isNonConstructableClassLikeInJsFile ?
6369+
[createConstructor(/*decorators*/ undefined, createModifiersFromModifierFlags(ModifierFlags.Private), [], /*body*/ undefined)] :
6370+
serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
63576371
for (const c of constructors) {
63586372
// A constructor's return type and type parameters are supposed to be controlled by the enclosing class declaration
63596373
// `signatureToSignatureDeclarationHelper` appends them regardless, so for now we delete them here
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
tests/cases/conformance/jsdoc/declarations/index.js(4,3): error TS2339: Property 'prototype' does not exist on type '{}'.
2+
3+
4+
==== tests/cases/conformance/jsdoc/declarations/index.js (1 errors) ====
5+
// https://github.com/microsoft/TypeScript/issues/35801
6+
let A;
7+
A = {};
8+
A.prototype.b = {};
9+
~~~~~~~~~
10+
!!! error TS2339: Property 'prototype' does not exist on type '{}'.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//// [index.js]
2+
// https://github.com/microsoft/TypeScript/issues/35801
3+
let A;
4+
A = {};
5+
A.prototype.b = {};
6+
7+
//// [index.js]
8+
// https://github.com/microsoft/TypeScript/issues/35801
9+
var A;
10+
A = {};
11+
A.prototype.b = {};
12+
13+
14+
//// [index.d.ts]
15+
declare class A {
16+
private constructor();
17+
b: {};
18+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
=== tests/cases/conformance/jsdoc/declarations/index.js ===
2+
// https://github.com/microsoft/TypeScript/issues/35801
3+
let A;
4+
>A : Symbol(A, Decl(index.js, 1, 3))
5+
6+
A = {};
7+
>A : Symbol(A, Decl(index.js, 1, 3))
8+
9+
A.prototype.b = {};
10+
>A.prototype : Symbol(A.b, Decl(index.js, 2, 7))
11+
>A : Symbol(A, Decl(index.js, 1, 3))
12+
>b : Symbol(A.b, Decl(index.js, 2, 7))
13+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/conformance/jsdoc/declarations/index.js ===
2+
// https://github.com/microsoft/TypeScript/issues/35801
3+
let A;
4+
>A : any
5+
6+
A = {};
7+
>A = {} : {}
8+
>A : any
9+
>{} : {}
10+
11+
A.prototype.b = {};
12+
>A.prototype.b = {} : {}
13+
>A.prototype.b : any
14+
>A.prototype : any
15+
>A : {}
16+
>prototype : any
17+
>b : any
18+
>{} : {}
19+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// @allowJs: true
2+
// @checkJs: true
3+
// @target: es5
4+
// @outDir: ./out
5+
// @declaration: true
6+
// @filename: index.js
7+
// https://github.com/microsoft/TypeScript/issues/35801
8+
let A;
9+
A = {};
10+
A.prototype.b = {};

0 commit comments

Comments
 (0)