Skip to content

Commit eb5789a

Browse files
committed
Add support for abstract constructor types
1 parent b977f86 commit eb5789a

33 files changed

+1214
-498
lines changed

src/compat/deprecations.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,23 @@ namespace ts {
164164
export const updateFunctionTypeNode = Debug.deprecate(factory.updateFunctionTypeNode, factoryDeprecation);
165165

166166
/** @deprecated Use `factory.createConstructorTypeNode` or the factory supplied by your transformation context instead. */
167-
export const createConstructorTypeNode = Debug.deprecate(factory.createConstructorTypeNode, factoryDeprecation);
167+
export const createConstructorTypeNode = Debug.deprecate((
168+
typeParameters: readonly TypeParameterDeclaration[] | undefined,
169+
parameters: readonly ParameterDeclaration[],
170+
type: TypeNode
171+
) => {
172+
return factory.createConstructorTypeNode(/*modifiers*/ undefined, typeParameters, parameters, type);
173+
}, factoryDeprecation);
168174

169175
/** @deprecated Use `factory.updateConstructorTypeNode` or the factory supplied by your transformation context instead. */
170-
export const updateConstructorTypeNode = Debug.deprecate(factory.updateConstructorTypeNode, factoryDeprecation);
176+
export const updateConstructorTypeNode = Debug.deprecate((
177+
node: ConstructorTypeNode,
178+
typeParameters: NodeArray<TypeParameterDeclaration> | undefined,
179+
parameters: NodeArray<ParameterDeclaration>,
180+
type: TypeNode
181+
) => {
182+
return factory.updateConstructorTypeNode(node, node.modifiers, typeParameters, parameters, type);
183+
}, factoryDeprecation);
171184

172185
/** @deprecated Use `factory.createTypeQueryNode` or the factory supplied by your transformation context instead. */
173186
export const createTypeQueryNode = Debug.deprecate(factory.createTypeQueryNode, factoryDeprecation);

src/compiler/checker.ts

Lines changed: 43 additions & 21 deletions
Large diffs are not rendered by default.

src/compiler/debug.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,10 @@ namespace ts {
367367
return formatEnum(flags, (<any>ts).TypeFlags, /*isFlags*/ true);
368368
}
369369

370+
export function formatSignatureFlags(flags: SignatureFlags | undefined): string {
371+
return formatEnum(flags, (<any>ts).SignatureFlags, /*isFlags*/ true);
372+
}
373+
370374
export function formatObjectFlags(flags: ObjectFlags | undefined): string {
371375
return formatEnum(flags, (<any>ts).ObjectFlags, /*isFlags*/ true);
372376
}
@@ -424,6 +428,11 @@ namespace ts {
424428
__debugTypeToString: { value(this: Type) { return this.checker.typeToString(this); } },
425429
});
426430

431+
Object.defineProperties(objectAllocator.getSignatureConstructor().prototype, {
432+
__debugFlags: { get(this: Signature) { return formatSignatureFlags(this.flags); } },
433+
__debugSignatureToString: { value(this: Signature) { return this.checker?.signatureToString(this) } }
434+
});
435+
427436
const nodeConstructors = [
428437
objectAllocator.getNodeConstructor(),
429438
objectAllocator.getIdentifierConstructor(),

src/compiler/emitter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2066,6 +2066,7 @@ namespace ts {
20662066

20672067
function emitConstructorType(node: ConstructorTypeNode) {
20682068
pushNameGenerationScope(node);
2069+
emitModifiers(node, node.modifiers);
20692070
writeKeyword("new");
20702071
writeSpace();
20712072
emitTypeParameters(node, node.typeParameters);

src/compiler/factory/nodeFactory.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1648,14 +1648,15 @@ namespace ts {
16481648

16491649
// @api
16501650
function createConstructorTypeNode(
1651+
modifiers: readonly Modifier[] | undefined,
16511652
typeParameters: readonly TypeParameterDeclaration[] | undefined,
16521653
parameters: readonly ParameterDeclaration[],
16531654
type: TypeNode | undefined
16541655
): ConstructorTypeNode {
16551656
const node = createBaseSignatureDeclaration<ConstructorTypeNode>(
16561657
SyntaxKind.ConstructorType,
16571658
/*decorators*/ undefined,
1658-
/*modifiers*/ undefined,
1659+
modifiers,
16591660
/*name*/ undefined,
16601661
typeParameters,
16611662
parameters,
@@ -1668,14 +1669,16 @@ namespace ts {
16681669
// @api
16691670
function updateConstructorTypeNode(
16701671
node: ConstructorTypeNode,
1672+
modifiers: readonly Modifier[] | undefined,
16711673
typeParameters: NodeArray<TypeParameterDeclaration> | undefined,
16721674
parameters: NodeArray<ParameterDeclaration>,
16731675
type: TypeNode | undefined
16741676
) {
1675-
return node.typeParameters !== typeParameters
1677+
return node.modifiers !== modifiers
1678+
|| node.typeParameters !== typeParameters
16761679
|| node.parameters !== parameters
16771680
|| node.type !== type
1678-
? updateBaseSignatureDeclaration(createConstructorTypeNode(typeParameters, parameters, type), node)
1681+
? updateBaseSignatureDeclaration(createConstructorTypeNode(modifiers, typeParameters, parameters, type), node)
16791682
: node;
16801683
}
16811684

src/compiler/parser.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3168,16 +3168,29 @@ namespace ts {
31683168
return finishNode(factory.createParenthesizedType(type), pos);
31693169
}
31703170

3171+
function parseModifiersForConstructorType(): NodeArray<Modifier> | undefined {
3172+
let modifiers: NodeArray<Modifier> | undefined;
3173+
if (token() === SyntaxKind.AbstractKeyword) {
3174+
const pos = getNodePos();
3175+
nextToken();
3176+
const modifier = finishNode(factory.createToken(SyntaxKind.AbstractKeyword), pos);
3177+
modifiers = createNodeArray<Modifier>([modifier], pos);
3178+
}
3179+
return modifiers;
3180+
}
3181+
31713182
function parseFunctionOrConstructorType(): TypeNode {
31723183
const pos = getNodePos();
31733184
const hasJSDoc = hasPrecedingJSDocComment();
3185+
const modifiers = parseModifiersForConstructorType();
31743186
const isConstructorType = parseOptional(SyntaxKind.NewKeyword);
31753187
const typeParameters = parseTypeParameters();
31763188
const parameters = parseParameters(SignatureFlags.Type);
31773189
const type = parseReturnType(SyntaxKind.EqualsGreaterThanToken, /*isType*/ false);
31783190
const node = isConstructorType
3179-
? factory.createConstructorTypeNode(typeParameters, parameters, type)
3191+
? factory.createConstructorTypeNode(modifiers, typeParameters, parameters, type)
31803192
: factory.createFunctionTypeNode(typeParameters, parameters, type);
3193+
if (!isConstructorType) (node as Mutable<Node>).modifiers = modifiers;
31813194
return withJSDoc(finishNode(node, pos), hasJSDoc);
31823195
}
31833196

@@ -3451,6 +3464,16 @@ namespace ts {
34513464
return token() === SyntaxKind.OpenParenToken && lookAhead(isUnambiguouslyStartOfFunctionType);
34523465
}
34533466

3467+
function nextTokenIsNewKeyword(): boolean {
3468+
nextToken();
3469+
return token() === SyntaxKind.NewKeyword;
3470+
}
3471+
3472+
function isStartOfConstructorType(): boolean {
3473+
return token() === SyntaxKind.NewKeyword ||
3474+
token() === SyntaxKind.AbstractKeyword && lookAhead(nextTokenIsNewKeyword);
3475+
}
3476+
34543477
function skipParameterStart(): boolean {
34553478
if (isModifierKind(token())) {
34563479
// Skip modifiers
@@ -3533,7 +3556,7 @@ namespace ts {
35333556
}
35343557

35353558
function parseTypeWorker(noConditionalTypes?: boolean): TypeNode {
3536-
if (isStartOfFunctionType() || token() === SyntaxKind.NewKeyword) {
3559+
if (isStartOfFunctionType() || isStartOfConstructorType()) {
35373560
return parseFunctionOrConstructorType();
35383561
}
35393562
const pos = getNodePos();

src/compiler/transformers/declarations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1009,7 +1009,7 @@ namespace ts {
10091009
return cleanup(factory.updateFunctionTypeNode(input, visitNodes(input.typeParameters, visitDeclarationSubtree), updateParamsList(input, input.parameters), visitNode(input.type, visitDeclarationSubtree)));
10101010
}
10111011
case SyntaxKind.ConstructorType: {
1012-
return cleanup(factory.updateConstructorTypeNode(input, visitNodes(input.typeParameters, visitDeclarationSubtree), updateParamsList(input, input.parameters), visitNode(input.type, visitDeclarationSubtree)));
1012+
return cleanup(factory.updateConstructorTypeNode(input, ensureModifiers(input), visitNodes(input.typeParameters, visitDeclarationSubtree), updateParamsList(input, input.parameters), visitNode(input.type, visitDeclarationSubtree)));
10131013
}
10141014
case SyntaxKind.ImportType: {
10151015
if (!isLiteralImportTypeNode(input)) return cleanup(input);

src/compiler/types.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5311,16 +5311,21 @@ namespace ts {
53115311
/* @internal */
53125312
export const enum SignatureFlags {
53135313
None = 0,
5314+
5315+
// Propagating flags
53145316
HasRestParameter = 1 << 0, // Indicates last parameter is rest parameter
53155317
HasLiteralTypes = 1 << 1, // Indicates signature is specialized
5316-
IsInnerCallChain = 1 << 2, // Indicates signature comes from a CallChain nested in an outer OptionalChain
5317-
IsOuterCallChain = 1 << 3, // Indicates signature comes from a CallChain that is the outermost chain of an optional expression
5318-
IsUntypedSignatureInJSFile = 1 << 4, // Indicates signature is from a js file and has no types
5318+
Abstract = 1 << 2, // Indicates signature comes from an abstract class, abstract construct signature, or abstract constructor type
5319+
5320+
// Non-propagating flags
5321+
IsInnerCallChain = 1 << 3, // Indicates signature comes from a CallChain nested in an outer OptionalChain
5322+
IsOuterCallChain = 1 << 4, // Indicates signature comes from a CallChain that is the outermost chain of an optional expression
5323+
IsUntypedSignatureInJSFile = 1 << 5, // Indicates signature is from a js file and has no types
53195324

5320-
// We do not propagate `IsInnerCallChain` to instantiated signatures, as that would result in us
5325+
// We do not propagate `IsInnerCallChain` or `IsOuterCallChain` to instantiated signatures, as that would result in us
53215326
// attempting to add `| undefined` on each recursive call to `getReturnTypeOfSignature` when
53225327
// instantiating the return type.
5323-
PropagatingFlags = HasRestParameter | HasLiteralTypes,
5328+
PropagatingFlags = HasRestParameter | HasLiteralTypes | Abstract,
53245329

53255330
CallChainFlags = IsInnerCallChain | IsOuterCallChain,
53265331
}
@@ -6666,8 +6671,8 @@ namespace ts {
66666671
updateTypeReferenceNode(node: TypeReferenceNode, typeName: EntityName, typeArguments: NodeArray<TypeNode> | undefined): TypeReferenceNode;
66676672
createFunctionTypeNode(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): FunctionTypeNode;
66686673
updateFunctionTypeNode(node: FunctionTypeNode, typeParameters: NodeArray<TypeParameterDeclaration> | undefined, parameters: NodeArray<ParameterDeclaration>, type: TypeNode): FunctionTypeNode;
6669-
createConstructorTypeNode(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): ConstructorTypeNode;
6670-
updateConstructorTypeNode(node: ConstructorTypeNode, typeParameters: NodeArray<TypeParameterDeclaration> | undefined, parameters: NodeArray<ParameterDeclaration>, type: TypeNode): ConstructorTypeNode;
6674+
createConstructorTypeNode(modifiers: readonly Modifier[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): ConstructorTypeNode;
6675+
updateConstructorTypeNode(node: ConstructorTypeNode, modifiers: readonly Modifier[] | undefined, typeParameters: NodeArray<TypeParameterDeclaration> | undefined, parameters: NodeArray<ParameterDeclaration>, type: TypeNode): ConstructorTypeNode;
66716676
createTypeQueryNode(exprName: EntityName): TypeQueryNode;
66726677
updateTypeQueryNode(node: TypeQueryNode, exprName: EntityName): TypeQueryNode;
66736678
createTypeLiteralNode(members: readonly TypeElement[] | undefined): TypeLiteralNode;

src/compiler/utilities.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4622,7 +4622,7 @@ namespace ts {
46224622
return flags;
46234623
}
46244624

4625-
export function modifiersToFlags(modifiers: NodeArray<Modifier> | undefined) {
4625+
export function modifiersToFlags(modifiers: readonly Modifier[] | undefined) {
46264626
let flags = ModifierFlags.None;
46274627
if (modifiers) {
46284628
for (const modifier of modifiers) {
@@ -5319,11 +5319,6 @@ namespace ts {
53195319
});
53205320
}
53215321

5322-
// Return true if the given type is the constructor type for an abstract class
5323-
export function isAbstractConstructorType(type: Type): boolean {
5324-
return !!(getObjectFlags(type) & ObjectFlags.Anonymous) && !!type.symbol && isAbstractConstructorSymbol(type.symbol);
5325-
}
5326-
53275322
export function isAbstractConstructorSymbol(symbol: Symbol): boolean {
53285323
if (symbol.flags & SymbolFlags.Class) {
53295324
const declaration = getClassLikeDeclarationOfSymbol(symbol);

src/compiler/visitorPublic.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,7 @@ namespace ts {
483483

484484
case SyntaxKind.ConstructorType:
485485
return factory.updateConstructorTypeNode(<ConstructorTypeNode>node,
486+
nodesVisitor((<ConstructorTypeNode>node).modifiers, visitor, isModifier),
486487
nodesVisitor((<ConstructorTypeNode>node).typeParameters, visitor, isTypeParameterDeclaration),
487488
nodesVisitor((<ConstructorTypeNode>node).parameters, visitor, isParameterDeclaration),
488489
nodeVisitor((<ConstructorTypeNode>node).type, visitor, isTypeNode));

src/lib/es5.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,7 +1496,7 @@ type Parameters<T extends (...args: any) => any> = T extends (...args: infer P)
14961496
/**
14971497
* Obtain the parameters of a constructor function type in a tuple
14981498
*/
1499-
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
1499+
type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;
15001500

15011501
/**
15021502
* Obtain the return type of a function type
@@ -1506,7 +1506,7 @@ type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => i
15061506
/**
15071507
* Obtain the return type of a constructor function type
15081508
*/
1509-
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
1509+
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
15101510

15111511
/**
15121512
* Marker for contextual 'this' type

src/services/symbolDisplay.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ namespace ts.SymbolDisplay {
194194
pushSymbolKind(symbolKind);
195195
displayParts.push(spacePart());
196196
if (useConstructSignatures) {
197+
if (signature.flags & SignatureFlags.Abstract) {
198+
displayParts.push(keywordPart(SyntaxKind.AbstractKeyword));
199+
displayParts.push(spacePart());
200+
}
197201
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
198202
displayParts.push(spacePart());
199203
}
@@ -219,6 +223,10 @@ namespace ts.SymbolDisplay {
219223
displayParts.push(lineBreakPart());
220224
}
221225
if (useConstructSignatures) {
226+
if (signature.flags & SignatureFlags.Abstract) {
227+
displayParts.push(keywordPart(SyntaxKind.AbstractKeyword));
228+
displayParts.push(spacePart());
229+
}
222230
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
223231
displayParts.push(spacePart());
224232
}

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3148,8 +3148,8 @@ declare namespace ts {
31483148
updateTypeReferenceNode(node: TypeReferenceNode, typeName: EntityName, typeArguments: NodeArray<TypeNode> | undefined): TypeReferenceNode;
31493149
createFunctionTypeNode(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): FunctionTypeNode;
31503150
updateFunctionTypeNode(node: FunctionTypeNode, typeParameters: NodeArray<TypeParameterDeclaration> | undefined, parameters: NodeArray<ParameterDeclaration>, type: TypeNode): FunctionTypeNode;
3151-
createConstructorTypeNode(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): ConstructorTypeNode;
3152-
updateConstructorTypeNode(node: ConstructorTypeNode, typeParameters: NodeArray<TypeParameterDeclaration> | undefined, parameters: NodeArray<ParameterDeclaration>, type: TypeNode): ConstructorTypeNode;
3151+
createConstructorTypeNode(modifiers: readonly Modifier[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): ConstructorTypeNode;
3152+
updateConstructorTypeNode(node: ConstructorTypeNode, modifiers: readonly Modifier[] | undefined, typeParameters: NodeArray<TypeParameterDeclaration> | undefined, parameters: NodeArray<ParameterDeclaration>, type: TypeNode): ConstructorTypeNode;
31533153
createTypeQueryNode(exprName: EntityName): TypeQueryNode;
31543154
updateTypeQueryNode(node: TypeQueryNode, exprName: EntityName): TypeQueryNode;
31553155
createTypeLiteralNode(members: readonly TypeElement[] | undefined): TypeLiteralNode;
@@ -10246,9 +10246,9 @@ declare namespace ts {
1024610246
/** @deprecated Use `factory.updateEnumDeclaration` or the factory supplied by your transformation context instead. */
1024710247
const updateEnumDeclaration: (node: EnumDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: Identifier, members: readonly EnumMember[]) => EnumDeclaration;
1024810248
/** @deprecated Use `factory.createModuleDeclaration` or the factory supplied by your transformation context instead. */
10249-
const createModuleDeclaration: (decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: ModuleName, body: Identifier | ModuleBlock | NamespaceDeclaration | JSDocNamespaceDeclaration | undefined, flags?: NodeFlags | undefined) => ModuleDeclaration;
10249+
const createModuleDeclaration: (decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: ModuleName, body: ModuleBlock | NamespaceDeclaration | Identifier | JSDocNamespaceDeclaration | undefined, flags?: NodeFlags | undefined) => ModuleDeclaration;
1025010250
/** @deprecated Use `factory.updateModuleDeclaration` or the factory supplied by your transformation context instead. */
10251-
const updateModuleDeclaration: (node: ModuleDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: ModuleName, body: Identifier | ModuleBlock | NamespaceDeclaration | JSDocNamespaceDeclaration | undefined) => ModuleDeclaration;
10251+
const updateModuleDeclaration: (node: ModuleDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: ModuleName, body: ModuleBlock | NamespaceDeclaration | Identifier | JSDocNamespaceDeclaration | undefined) => ModuleDeclaration;
1025210252
/** @deprecated Use `factory.createModuleBlock` or the factory supplied by your transformation context instead. */
1025310253
const createModuleBlock: (statements: readonly Statement[]) => ModuleBlock;
1025410254
/** @deprecated Use `factory.updateModuleBlock` or the factory supplied by your transformation context instead. */

0 commit comments

Comments
 (0)