Skip to content

Commit 136dbf0

Browse files
committed
Add support for abstract constructor types and signatures
1 parent 75f88ee commit 136dbf0

30 files changed

+1229
-541
lines changed

src/compiler/checker.ts

+86-44
Original file line numberDiff line numberDiff line change
@@ -4590,8 +4590,12 @@ namespace ts {
45904590
returnTypeNode = createKeywordTypeNode(SyntaxKind.AnyKeyword);
45914591
}
45924592
}
4593+
let modifiers: Modifier[] | undefined;
4594+
if ((kind === SyntaxKind.ConstructorType || kind === SyntaxKind.ConstructSignature) && signature.flags & SignatureFlags.Abstract) {
4595+
modifiers = createModifiersFromModifierFlags(ModifierFlags.Abstract);
4596+
}
45934597
context.approximateLength += 3; // Usually a signature contributes a few more characters than this, but 3 is the minimum
4594-
return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode, typeArguments);
4598+
return createSignatureDeclaration(kind, modifiers, typeParameters, parameters, returnTypeNode, typeArguments);
45954599
}
45964600

45974601
function typeParameterToDeclarationWithConstraint(type: TypeParameter, context: NodeBuilderContext, constraintNode: TypeNode | undefined): TypeParameterDeclaration {
@@ -8994,8 +8998,10 @@ namespace ts {
89948998
function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
89958999
const baseConstructorType = getBaseConstructorTypeOfClass(classType);
89969000
const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct);
9001+
const declaration = getClassLikeDeclarationOfSymbol(classType.symbol);
9002+
const isAbstract = !!declaration && hasModifier(declaration, ModifierFlags.Abstract);
89979003
if (baseSignatures.length === 0) {
8998-
return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None)];
9004+
return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*resolvedTypePredicate*/ undefined, 0, isAbstract ? SignatureFlags.Abstract : SignatureFlags.None)];
89999005
}
90009006
const baseTypeNode = getBaseTypeNodeOfClass(classType)!;
90019007
const isJavaScript = isInJSFile(baseTypeNode);
@@ -9009,6 +9015,7 @@ namespace ts {
90099015
const sig = typeParamCount ? createSignatureInstantiation(baseSig, fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, isJavaScript)) : cloneSignature(baseSig);
90109016
sig.typeParameters = classType.localTypeParameters;
90119017
sig.resolvedReturnType = classType;
9018+
sig.flags = isAbstract ? sig.flags | SignatureFlags.Abstract : sig.flags & ~SignatureFlags.Abstract;
90129019
result.push(sig);
90139020
}
90149021
}
@@ -10403,6 +10410,10 @@ namespace ts {
1040310410
if (hasRestParameter(declaration) || isInJSFile(declaration) && maybeAddJsSyntheticRestParameter(declaration, parameters)) {
1040410411
flags |= SignatureFlags.HasRestParameter;
1040510412
}
10413+
if ((isConstructSignatureDeclaration(declaration) || isConstructorTypeNode(declaration)) && hasModifier(declaration, ModifierFlags.Abstract) ||
10414+
isConstructorDeclaration(declaration) && hasModifier(declaration.parent, ModifierFlags.Abstract)) {
10415+
flags |= SignatureFlags.Abstract;
10416+
}
1040610417
links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters,
1040710418
/*resolvedReturnType*/ undefined, /*resolvedTypePredicate*/ undefined,
1040810419
minArgumentCount, flags);
@@ -16174,7 +16185,9 @@ namespace ts {
1617416185
SignatureKind.Call : kind);
1617516186

1617616187
if (kind === SignatureKind.Construct && sourceSignatures.length && targetSignatures.length) {
16177-
if (isAbstractConstructorType(source) && !isAbstractConstructorType(target)) {
16188+
const sourceIsAbstract = !!(sourceSignatures[0].flags & SignatureFlags.Abstract);
16189+
const targetIsAbstract = !!(targetSignatures[0].flags & SignatureFlags.Abstract);
16190+
if (sourceIsAbstract && !targetIsAbstract) {
1617816191
// An abstract constructor type is not assignable to a non-abstract constructor type
1617916192
// as it would otherwise be possible to new an abstract class. Note that the assignability
1618016193
// check we perform for an extends clause excludes construct signatures from the target,
@@ -25067,8 +25080,7 @@ namespace ts {
2506725080
// Note, only class declarations can be declared abstract.
2506825081
// In the case of a merged class-module or class-interface declaration,
2506925082
// only the class declaration node will have the Abstract flag set.
25070-
const valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol);
25071-
if (valueDecl && hasModifier(valueDecl, ModifierFlags.Abstract)) {
25083+
if (constructSignatures[0].flags & SignatureFlags.Abstract) {
2507225084
error(node, Diagnostics.Cannot_create_an_instance_of_an_abstract_class);
2507325085
return resolveErrorCall(node);
2507425086
}
@@ -28687,6 +28699,33 @@ namespace ts {
2868728699
}
2868828700
}
2868928701

28702+
function checkConstructSignatureDeclaration(node: ConstructSignatureDeclaration) {
28703+
checkSignatureDeclaration(node);
28704+
if (produceDiagnostics) {
28705+
const symbol = getSymbolOfNode(node);
28706+
const firstDeclaration = getDeclarationOfKind(symbol, node.kind);
28707+
if (node === firstDeclaration) {
28708+
const flagsToCheck: ModifierFlags = ModifierFlags.Abstract;
28709+
let hasOverloads = false;
28710+
let someNodeFlags = ModifierFlags.None;
28711+
let allNodeFlags = flagsToCheck;
28712+
for (const declaration of symbol.declarations) {
28713+
if (isConstructSignatureDeclaration(declaration)) {
28714+
const currentNodeFlags = getEffectiveDeclarationFlags(declaration, flagsToCheck);
28715+
someNodeFlags |= currentNodeFlags;
28716+
allNodeFlags &= currentNodeFlags;
28717+
if (declaration !== node) {
28718+
hasOverloads = true;
28719+
}
28720+
}
28721+
}
28722+
if (hasOverloads) {
28723+
checkFlagAgreementBetweenOverloads(symbol.declarations, /*implementation*/ undefined, flagsToCheck, someNodeFlags, allNodeFlags);
28724+
}
28725+
}
28726+
}
28727+
}
28728+
2869028729
function checkAccessorDeclaration(node: AccessorDeclaration) {
2869128730
if (produceDiagnostics) {
2869228731
// Grammar checking accessors
@@ -28966,44 +29005,43 @@ namespace ts {
2896629005
return flags & flagsToCheck;
2896729006
}
2896829007

28969-
function checkFunctionOrConstructorSymbol(symbol: Symbol): void {
28970-
if (!produceDiagnostics) {
28971-
return;
28972-
}
29008+
function getCanonicalOverload(overloads: Declaration[], implementation: FunctionLikeDeclaration | undefined): Declaration {
29009+
// Consider the canonical set of flags to be the flags of the bodyDeclaration or the first declaration
29010+
// Error on all deviations from this canonical set of flags
29011+
// The caveat is that if some overloads are defined in lib.d.ts, we don't want to
29012+
// report the errors on those. To achieve this, we will say that the implementation is
29013+
// the canonical signature only if it is in the same container as the first overload
29014+
const implementationSharesContainerWithFirstOverload = implementation !== undefined && implementation.parent === overloads[0].parent;
29015+
return implementationSharesContainerWithFirstOverload ? implementation! : overloads[0];
29016+
}
2897329017

28974-
function getCanonicalOverload(overloads: Declaration[], implementation: FunctionLikeDeclaration | undefined): Declaration {
28975-
// Consider the canonical set of flags to be the flags of the bodyDeclaration or the first declaration
28976-
// Error on all deviations from this canonical set of flags
28977-
// The caveat is that if some overloads are defined in lib.d.ts, we don't want to
28978-
// report the errors on those. To achieve this, we will say that the implementation is
28979-
// the canonical signature only if it is in the same container as the first overload
28980-
const implementationSharesContainerWithFirstOverload = implementation !== undefined && implementation.parent === overloads[0].parent;
28981-
return implementationSharesContainerWithFirstOverload ? implementation! : overloads[0];
29018+
function checkFlagAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration | undefined, flagsToCheck: ModifierFlags, someOverloadFlags: ModifierFlags, allOverloadFlags: ModifierFlags): void {
29019+
// Error if some overloads have a flag that is not shared by all overloads. To find the
29020+
// deviations, we XOR someOverloadFlags with allOverloadFlags
29021+
const someButNotAllOverloadFlags = someOverloadFlags ^ allOverloadFlags;
29022+
if (someButNotAllOverloadFlags !== 0) {
29023+
const canonicalFlags = getEffectiveDeclarationFlags(getCanonicalOverload(overloads, implementation), flagsToCheck);
29024+
forEach(overloads, o => {
29025+
const deviation = getEffectiveDeclarationFlags(o, flagsToCheck) ^ canonicalFlags;
29026+
if (deviation & ModifierFlags.Export) {
29027+
error(getNameOfDeclaration(o) ?? o, Diagnostics.Overload_signatures_must_all_be_exported_or_non_exported);
29028+
}
29029+
else if (deviation & ModifierFlags.Ambient) {
29030+
error(getNameOfDeclaration(o) ?? o, Diagnostics.Overload_signatures_must_all_be_ambient_or_non_ambient);
29031+
}
29032+
else if (deviation & (ModifierFlags.Private | ModifierFlags.Protected)) {
29033+
error(getNameOfDeclaration(o) ?? o, Diagnostics.Overload_signatures_must_all_be_public_private_or_protected);
29034+
}
29035+
else if (deviation & ModifierFlags.Abstract) {
29036+
error(getNameOfDeclaration(o) ?? o, Diagnostics.Overload_signatures_must_all_be_abstract_or_non_abstract);
29037+
}
29038+
});
2898229039
}
29040+
}
2898329041

28984-
function checkFlagAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration | undefined, flagsToCheck: ModifierFlags, someOverloadFlags: ModifierFlags, allOverloadFlags: ModifierFlags): void {
28985-
// Error if some overloads have a flag that is not shared by all overloads. To find the
28986-
// deviations, we XOR someOverloadFlags with allOverloadFlags
28987-
const someButNotAllOverloadFlags = someOverloadFlags ^ allOverloadFlags;
28988-
if (someButNotAllOverloadFlags !== 0) {
28989-
const canonicalFlags = getEffectiveDeclarationFlags(getCanonicalOverload(overloads, implementation), flagsToCheck);
28990-
28991-
forEach(overloads, o => {
28992-
const deviation = getEffectiveDeclarationFlags(o, flagsToCheck) ^ canonicalFlags;
28993-
if (deviation & ModifierFlags.Export) {
28994-
error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_exported_or_non_exported);
28995-
}
28996-
else if (deviation & ModifierFlags.Ambient) {
28997-
error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_ambient_or_non_ambient);
28998-
}
28999-
else if (deviation & (ModifierFlags.Private | ModifierFlags.Protected)) {
29000-
error(getNameOfDeclaration(o) || o, Diagnostics.Overload_signatures_must_all_be_public_private_or_protected);
29001-
}
29002-
else if (deviation & ModifierFlags.Abstract) {
29003-
error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_abstract_or_non_abstract);
29004-
}
29005-
});
29006-
}
29042+
function checkFunctionOrConstructorSymbol(symbol: Symbol): void {
29043+
if (!produceDiagnostics) {
29044+
return;
2900729045
}
2900829046

2900929047
function checkQuestionTokenAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration | undefined, someHaveQuestionToken: boolean, allHaveQuestionToken: boolean): void {
@@ -33331,10 +33369,11 @@ namespace ts {
3333133369
return checkPropertyDeclaration(<PropertyDeclaration>node);
3333233370
case SyntaxKind.PropertySignature:
3333333371
return checkPropertySignature(<PropertySignature>node);
33334-
case SyntaxKind.FunctionType:
33372+
case SyntaxKind.ConstructSignature:
33373+
return checkConstructSignatureDeclaration(<ConstructSignatureDeclaration>node);
3333533374
case SyntaxKind.ConstructorType:
33375+
case SyntaxKind.FunctionType:
3333633376
case SyntaxKind.CallSignature:
33337-
case SyntaxKind.ConstructSignature:
3333833377
case SyntaxKind.IndexSignature:
3333933378
return checkSignatureDeclaration(<SignatureDeclaration>node);
3334033379
case SyntaxKind.MethodDeclaration:
@@ -35469,7 +35508,9 @@ namespace ts {
3546935508
if (flags & ModifierFlags.Abstract) {
3547035509
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "abstract");
3547135510
}
35472-
if (node.kind !== SyntaxKind.ClassDeclaration) {
35511+
if (node.kind !== SyntaxKind.ClassDeclaration &&
35512+
node.kind !== SyntaxKind.ConstructorType &&
35513+
node.kind !== SyntaxKind.ConstructSignature) {
3547335514
if (node.kind !== SyntaxKind.MethodDeclaration &&
3547435515
node.kind !== SyntaxKind.PropertyDeclaration &&
3547535516
node.kind !== SyntaxKind.GetAccessor &&
@@ -35580,6 +35621,8 @@ namespace ts {
3558035621
case SyntaxKind.FunctionDeclaration:
3558135622
return nodeHasAnyModifiersExcept(node, SyntaxKind.AsyncKeyword);
3558235623
case SyntaxKind.ClassDeclaration:
35624+
case SyntaxKind.ConstructorType:
35625+
case SyntaxKind.ConstructSignature:
3558335626
return nodeHasAnyModifiersExcept(node, SyntaxKind.AbstractKeyword);
3558435627
case SyntaxKind.InterfaceDeclaration:
3558535628
case SyntaxKind.VariableStatement:
@@ -35589,7 +35632,6 @@ namespace ts {
3558935632
return nodeHasAnyModifiersExcept(node, SyntaxKind.ConstKeyword);
3559035633
default:
3559135634
Debug.fail();
35592-
return false;
3559335635
}
3559435636
}
3559535637
}

src/compiler/emitter.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2048,6 +2048,7 @@ namespace ts {
20482048

20492049
function emitConstructorType(node: ConstructorTypeNode) {
20502050
pushNameGenerationScope(node);
2051+
emitModifiers(node, node.modifiers);
20512052
writeKeyword("new");
20522053
writeSpace();
20532054
emitTypeParameters(node, node.typeParameters);

0 commit comments

Comments
 (0)