Fix crash in JS declaration emit (#38508)

* Fix crash in JS decl emit

* Emit as class with private ctor
This commit is contained in:
Ron Buckton 2020-05-15 14:00:59 -07:00 committed by GitHub
parent 7fc456f2d7
commit 1cbe7ef000
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 86 additions and 2 deletions

View File

@ -5949,6 +5949,7 @@ namespace ts {
}
}
// Synthesize declarations for a symbol - might be an Interface, a Class, a Namespace, a Type, a Variable (const, let, or var), an Alias
// or a merge of some number of those.
// An interesting challenge is ensuring that when classes merge with namespaces and interfaces, is keeping
@ -6317,7 +6318,10 @@ namespace ts {
const baseTypes = getBaseTypes(classType);
const implementsTypes = getImplementsTypes(classType);
const staticType = getTypeOfSymbol(symbol);
const staticBaseType = getBaseConstructorTypeOfClass(staticType as InterfaceType);
const isClass = !!staticType.symbol?.valueDeclaration && isClassLike(staticType.symbol.valueDeclaration);
const staticBaseType = isClass
? getBaseConstructorTypeOfClass(staticType as InterfaceType)
: anyType;
const heritageClauses = [
...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
@ -6353,7 +6357,17 @@ namespace ts {
const staticMembers = flatMap(
filter(getPropertiesOfType(staticType), p => !(p.flags & SymbolFlags.Prototype) && p.escapedName !== "prototype" && !isNamespaceMember(p)),
p => serializePropertySymbolForClass(p, /*isStatic*/ true, staticBaseType));
const constructors = serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
// When we encounter an `X.prototype.y` assignment in a JS file, we bind `X` as a class regardless as to whether
// the value is ever initialized with a class or function-like value. For cases where `X` could never be
// created via `new`, we will inject a `private constructor()` declaration to indicate it is not createable.
const isNonConstructableClassLikeInJsFile =
!isClass &&
!!symbol.valueDeclaration &&
isInJSFile(symbol.valueDeclaration) &&
!some(getSignaturesOfType(staticType, SignatureKind.Construct));
const constructors = isNonConstructableClassLikeInJsFile ?
[createConstructor(/*decorators*/ undefined, createModifiersFromModifierFlags(ModifierFlags.Private), [], /*body*/ undefined)] :
serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
for (const c of constructors) {
// A constructor's return type and type parameters are supposed to be controlled by the enclosing class declaration
// `signatureToSignatureDeclarationHelper` appends them regardless, so for now we delete them here

View File

@ -0,0 +1,10 @@
tests/cases/conformance/jsdoc/declarations/index.js(4,3): error TS2339: Property 'prototype' does not exist on type '{}'.
==== tests/cases/conformance/jsdoc/declarations/index.js (1 errors) ====
// https://github.com/microsoft/TypeScript/issues/35801
let A;
A = {};
A.prototype.b = {};
~~~~~~~~~
!!! error TS2339: Property 'prototype' does not exist on type '{}'.

View File

@ -0,0 +1,18 @@
//// [index.js]
// https://github.com/microsoft/TypeScript/issues/35801
let A;
A = {};
A.prototype.b = {};
//// [index.js]
// https://github.com/microsoft/TypeScript/issues/35801
var A;
A = {};
A.prototype.b = {};
//// [index.d.ts]
declare class A {
private constructor();
b: {};
}

View File

@ -0,0 +1,13 @@
=== tests/cases/conformance/jsdoc/declarations/index.js ===
// https://github.com/microsoft/TypeScript/issues/35801
let A;
>A : Symbol(A, Decl(index.js, 1, 3))
A = {};
>A : Symbol(A, Decl(index.js, 1, 3))
A.prototype.b = {};
>A.prototype : Symbol(A.b, Decl(index.js, 2, 7))
>A : Symbol(A, Decl(index.js, 1, 3))
>b : Symbol(A.b, Decl(index.js, 2, 7))

View File

@ -0,0 +1,19 @@
=== tests/cases/conformance/jsdoc/declarations/index.js ===
// https://github.com/microsoft/TypeScript/issues/35801
let A;
>A : any
A = {};
>A = {} : {}
>A : any
>{} : {}
A.prototype.b = {};
>A.prototype.b = {} : {}
>A.prototype.b : any
>A.prototype : any
>A : {}
>prototype : any
>b : any
>{} : {}

View File

@ -0,0 +1,10 @@
// @allowJs: true
// @checkJs: true
// @target: es5
// @outDir: ./out
// @declaration: true
// @filename: index.js
// https://github.com/microsoft/TypeScript/issues/35801
let A;
A = {};
A.prototype.b = {};