Skip to content

Commit

Permalink
Add support for abstract constructor types
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Jan 28, 2020
1 parent 75f88ee commit 757b70f
Show file tree
Hide file tree
Showing 31 changed files with 1,222 additions and 498 deletions.
39 changes: 27 additions & 12 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4590,8 +4590,12 @@ namespace ts {
returnTypeNode = createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
}
let modifiers: Modifier[] | undefined;
if ((kind === SyntaxKind.ConstructorType) && signature.flags & SignatureFlags.Abstract) {
modifiers = createModifiersFromModifierFlags(ModifierFlags.Abstract);
}
context.approximateLength += 3; // Usually a signature contributes a few more characters than this, but 3 is the minimum
return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode, typeArguments);
return createSignatureDeclaration(kind, modifiers, typeParameters, parameters, returnTypeNode, typeArguments);
}

function typeParameterToDeclarationWithConstraint(type: TypeParameter, context: NodeBuilderContext, constraintNode: TypeNode | undefined): TypeParameterDeclaration {
Expand Down Expand Up @@ -8013,7 +8017,10 @@ namespace ts {
const signatures = getSignaturesOfType(type, SignatureKind.Construct);
if (signatures.length === 1) {
const s = signatures[0];
return !s.typeParameters && s.parameters.length === 1 && signatureHasRestParameter(s) && getElementTypeOfArrayType(getTypeOfParameter(s.parameters[0])) === anyType;
if (!s.typeParameters && s.parameters.length === 1 && signatureHasRestParameter(s)) {
const paramType = getTypeOfParameter(s.parameters[0]);
return isTypeAny(paramType) || getElementTypeOfArrayType(paramType) === anyType;
}
}
return false;
}
Expand Down Expand Up @@ -8994,8 +9001,10 @@ namespace ts {
function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
const baseConstructorType = getBaseConstructorTypeOfClass(classType);
const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct);
const declaration = getClassLikeDeclarationOfSymbol(classType.symbol);
const isAbstract = !!declaration && hasModifier(declaration, ModifierFlags.Abstract);
if (baseSignatures.length === 0) {
return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None)];
return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*resolvedTypePredicate*/ undefined, 0, isAbstract ? SignatureFlags.Abstract : SignatureFlags.None)];
}
const baseTypeNode = getBaseTypeNodeOfClass(classType)!;
const isJavaScript = isInJSFile(baseTypeNode);
Expand All @@ -9009,6 +9018,7 @@ namespace ts {
const sig = typeParamCount ? createSignatureInstantiation(baseSig, fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, isJavaScript)) : cloneSignature(baseSig);
sig.typeParameters = classType.localTypeParameters;
sig.resolvedReturnType = classType;
sig.flags = isAbstract ? sig.flags | SignatureFlags.Abstract : sig.flags & ~SignatureFlags.Abstract;
result.push(sig);
}
}
Expand Down Expand Up @@ -10403,6 +10413,10 @@ namespace ts {
if (hasRestParameter(declaration) || isInJSFile(declaration) && maybeAddJsSyntheticRestParameter(declaration, parameters)) {
flags |= SignatureFlags.HasRestParameter;
}
if (isConstructorTypeNode(declaration) && hasModifier(declaration, ModifierFlags.Abstract) ||
isConstructorDeclaration(declaration) && hasModifier(declaration.parent, ModifierFlags.Abstract)) {
flags |= SignatureFlags.Abstract;
}
links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters,
/*resolvedReturnType*/ undefined, /*resolvedTypePredicate*/ undefined,
minArgumentCount, flags);
Expand Down Expand Up @@ -16174,7 +16188,9 @@ namespace ts {
SignatureKind.Call : kind);

if (kind === SignatureKind.Construct && sourceSignatures.length && targetSignatures.length) {
if (isAbstractConstructorType(source) && !isAbstractConstructorType(target)) {
const sourceIsAbstract = !!(sourceSignatures[0].flags & SignatureFlags.Abstract);
const targetIsAbstract = !!(targetSignatures[0].flags & SignatureFlags.Abstract);
if (sourceIsAbstract && !targetIsAbstract) {
// An abstract constructor type is not assignable to a non-abstract constructor type
// as it would otherwise be possible to new an abstract class. Note that the assignability
// check we perform for an extends clause excludes construct signatures from the target,
Expand Down Expand Up @@ -25067,8 +25083,7 @@ namespace ts {
// Note, only class declarations can be declared abstract.
// In the case of a merged class-module or class-interface declaration,
// only the class declaration node will have the Abstract flag set.
const valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol);
if (valueDecl && hasModifier(valueDecl, ModifierFlags.Abstract)) {
if (constructSignatures[0].flags & SignatureFlags.Abstract) {
error(node, Diagnostics.Cannot_create_an_instance_of_an_abstract_class);
return resolveErrorCall(node);
}
Expand Down Expand Up @@ -28980,14 +28995,13 @@ namespace ts {
const implementationSharesContainerWithFirstOverload = implementation !== undefined && implementation.parent === overloads[0].parent;
return implementationSharesContainerWithFirstOverload ? implementation! : overloads[0];
}

function checkFlagAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration | undefined, flagsToCheck: ModifierFlags, someOverloadFlags: ModifierFlags, allOverloadFlags: ModifierFlags): void {
// Error if some overloads have a flag that is not shared by all overloads. To find the
// deviations, we XOR someOverloadFlags with allOverloadFlags
const someButNotAllOverloadFlags = someOverloadFlags ^ allOverloadFlags;
if (someButNotAllOverloadFlags !== 0) {
const canonicalFlags = getEffectiveDeclarationFlags(getCanonicalOverload(overloads, implementation), flagsToCheck);

forEach(overloads, o => {
const deviation = getEffectiveDeclarationFlags(o, flagsToCheck) ^ canonicalFlags;
if (deviation & ModifierFlags.Export) {
Expand All @@ -29005,7 +29019,7 @@ namespace ts {
});
}
}

function checkQuestionTokenAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration | undefined, someHaveQuestionToken: boolean, allHaveQuestionToken: boolean): void {
if (someHaveQuestionToken !== allHaveQuestionToken) {
const canonicalHasQuestionToken = hasQuestionToken(getCanonicalOverload(overloads, implementation));
Expand Down Expand Up @@ -33331,8 +33345,8 @@ namespace ts {
return checkPropertyDeclaration(<PropertyDeclaration>node);
case SyntaxKind.PropertySignature:
return checkPropertySignature(<PropertySignature>node);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.FunctionType:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
Expand Down Expand Up @@ -35469,7 +35483,8 @@ namespace ts {
if (flags & ModifierFlags.Abstract) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "abstract");
}
if (node.kind !== SyntaxKind.ClassDeclaration) {
if (node.kind !== SyntaxKind.ClassDeclaration &&
node.kind !== SyntaxKind.ConstructorType) {
if (node.kind !== SyntaxKind.MethodDeclaration &&
node.kind !== SyntaxKind.PropertyDeclaration &&
node.kind !== SyntaxKind.GetAccessor &&
Expand Down Expand Up @@ -35580,6 +35595,7 @@ namespace ts {
case SyntaxKind.FunctionDeclaration:
return nodeHasAnyModifiersExcept(node, SyntaxKind.AsyncKeyword);
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ConstructorType:
return nodeHasAnyModifiersExcept(node, SyntaxKind.AbstractKeyword);
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.VariableStatement:
Expand All @@ -35589,7 +35605,6 @@ namespace ts {
return nodeHasAnyModifiersExcept(node, SyntaxKind.ConstKeyword);
default:
Debug.fail();
return false;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2048,6 +2048,7 @@ namespace ts {

function emitConstructorType(node: ConstructorTypeNode) {
pushNameGenerationScope(node);
emitModifiers(node, node.modifiers);
writeKeyword("new");
writeSpace();
emitTypeParameters(node, node.typeParameters);
Expand Down
75 changes: 54 additions & 21 deletions src/compiler/factoryPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ namespace ts {
type: TypeNode | undefined,
name: string | PropertyName,
questionToken: QuestionToken | undefined) {
const node = createSignatureDeclaration(SyntaxKind.MethodSignature, typeParameters, parameters, type) as MethodSignature;
const node = createSignatureDeclaration(SyntaxKind.MethodSignature, /*modifiers*/ undefined, typeParameters, parameters, type) as MethodSignature;
node.name = asName(name);
node.questionToken = questionToken;
return node;
Expand Down Expand Up @@ -651,19 +651,19 @@ namespace ts {
}

export function createCallSignature(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined) {
return createSignatureDeclaration(SyntaxKind.CallSignature, typeParameters, parameters, type) as CallSignatureDeclaration;
return createSignatureDeclaration(SyntaxKind.CallSignature, /*modifiers*/ undefined, typeParameters, parameters, type) as CallSignatureDeclaration;
}

export function updateCallSignature(node: CallSignatureDeclaration, typeParameters: NodeArray<TypeParameterDeclaration> | undefined, parameters: NodeArray<ParameterDeclaration>, type: TypeNode | undefined) {
return updateSignatureDeclaration(node, typeParameters, parameters, type);
return updateSignatureDeclaration(node, /*modifiers*/ undefined, typeParameters, parameters, type);
}

export function createConstructSignature(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined) {
return createSignatureDeclaration(SyntaxKind.ConstructSignature, typeParameters, parameters, type) as ConstructSignatureDeclaration;
export function createConstructSignature(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined): ConstructSignatureDeclaration {
return createSignatureDeclaration(SyntaxKind.ConstructSignature, /*modifiers*/ undefined, typeParameters, parameters, type) as ConstructSignatureDeclaration;
}

export function updateConstructSignature(node: ConstructSignatureDeclaration, typeParameters: NodeArray<TypeParameterDeclaration> | undefined, parameters: NodeArray<ParameterDeclaration>, type: TypeNode | undefined) {
return updateSignatureDeclaration(node, typeParameters, parameters, type);
export function updateConstructSignature(node: ConstructSignatureDeclaration, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined): ConstructSignatureDeclaration {
return updateSignatureDeclaration(node, /*modifiers*/ undefined, typeParameters, parameters, type);
}

export function createIndexSignature(
Expand Down Expand Up @@ -694,20 +694,22 @@ namespace ts {
}

/* @internal */
export function createSignatureDeclaration(kind: SyntaxKind, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, typeArguments?: readonly TypeNode[] | undefined) {
export function createSignatureDeclaration(kind: SyntaxKind, modifiers: readonly Modifier[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, typeArguments?: readonly TypeNode[] | undefined) {
const node = createSynthesizedNode(kind) as SignatureDeclaration;
node.modifiers = asNodeArray(modifiers);
node.typeParameters = asNodeArray(typeParameters);
node.parameters = asNodeArray(parameters);
node.type = type;
node.typeArguments = asNodeArray(typeArguments);
return node;
}

function updateSignatureDeclaration<T extends SignatureDeclaration>(node: T, typeParameters: NodeArray<TypeParameterDeclaration> | undefined, parameters: NodeArray<ParameterDeclaration>, type: TypeNode | undefined): T {
return node.typeParameters !== typeParameters
function updateSignatureDeclaration<T extends SignatureDeclaration>(node: T, modifiers: readonly Modifier[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined): T {
return node.modifiers !== modifiers
|| node.typeParameters !== typeParameters
|| node.parameters !== parameters
|| node.type !== type
? updateNode(<T>createSignatureDeclaration(node.kind, typeParameters, parameters, type), node)
? updateNode(<T>createSignatureDeclaration(node.kind, modifiers, typeParameters, parameters, type), node)
: node;
}

Expand Down Expand Up @@ -756,19 +758,50 @@ namespace ts {
}

export function createFunctionTypeNode(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined) {
return createSignatureDeclaration(SyntaxKind.FunctionType, typeParameters, parameters, type) as FunctionTypeNode;
return createSignatureDeclaration(SyntaxKind.FunctionType, /*modifiers*/ undefined, typeParameters, parameters, type) as FunctionTypeNode;
}

export function updateFunctionTypeNode(node: FunctionTypeNode, typeParameters: NodeArray<TypeParameterDeclaration> | undefined, parameters: NodeArray<ParameterDeclaration>, type: TypeNode | undefined) {
return updateSignatureDeclaration(node, typeParameters, parameters, type);
}

export function createConstructorTypeNode(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined) {
return createSignatureDeclaration(SyntaxKind.ConstructorType, typeParameters, parameters, type) as ConstructorTypeNode;
}

export function updateConstructorTypeNode(node: ConstructorTypeNode, typeParameters: NodeArray<TypeParameterDeclaration> | undefined, parameters: NodeArray<ParameterDeclaration>, type: TypeNode | undefined) {
return updateSignatureDeclaration(node, typeParameters, parameters, type);
return updateSignatureDeclaration(node, /*modifiers*/ undefined, typeParameters, parameters, type);
}

export function createConstructorTypeNode(modifiers: readonly Modifier[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined): ConstructorTypeNode;
export function createConstructorTypeNode(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined): ConstructorTypeNode;
export function createConstructorTypeNode(...args:
| [readonly Modifier[] | undefined, readonly TypeParameterDeclaration[] | undefined, readonly ParameterDeclaration[], TypeNode | undefined]
| [readonly TypeParameterDeclaration[] | undefined, readonly ParameterDeclaration[], TypeNode | undefined]
) {
let modifiers: readonly Modifier[] | undefined;
let typeParameters: readonly TypeParameterDeclaration[] | undefined;
let parameters: readonly ParameterDeclaration[];
let type: TypeNode | undefined;
if (args.length === 4) {
[modifiers, typeParameters, parameters, type] = args;
}
else {
[typeParameters, parameters, type] = args;
}
return createSignatureDeclaration(SyntaxKind.ConstructorType, modifiers, typeParameters, parameters, type) as ConstructorTypeNode;
}

export function updateConstructorTypeNode(node: ConstructorTypeNode, modifiers: readonly Modifier[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined): ConstructorTypeNode;
export function updateConstructorTypeNode(node: ConstructorTypeNode, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined): ConstructorTypeNode;
export function updateConstructorTypeNode(node: ConstructorTypeNode, ...args:
| [readonly Modifier[] | undefined, readonly TypeParameterDeclaration[] | undefined, readonly ParameterDeclaration[], TypeNode | undefined]
| [readonly TypeParameterDeclaration[] | undefined, readonly ParameterDeclaration[], TypeNode | undefined]
) {
let modifiers: readonly Modifier[] | undefined;
let typeParameters: readonly TypeParameterDeclaration[] | undefined;
let parameters: readonly ParameterDeclaration[];
let type: TypeNode | undefined;
if (args.length === 4) {
[modifiers, typeParameters, parameters, type] = args;
}
else {
[typeParameters, parameters, type] = args;
modifiers = node.modifiers;
}
return updateSignatureDeclaration(node, modifiers, typeParameters, parameters, type);
}

export function createTypeQueryNode(exprName: EntityName) {
Expand Down
Loading

0 comments on commit 757b70f

Please sign in to comment.