Skip to content

Commit 76573c6

Browse files
author
Andy
authored
getEffectiveTypeParameterDeclarations: Always return a defined result (#24251)
1 parent 89059f0 commit 76573c6

File tree

5 files changed

+29
-46
lines changed

5 files changed

+29
-46
lines changed

src/compiler/checker.ts

+16-19
Original file line numberDiff line numberDiff line change
@@ -5165,7 +5165,7 @@ namespace ts {
51655165
// Appends the type parameters given by a list of declarations to a set of type parameters and returns the resulting set.
51665166
// The function allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set
51675167
// in-place and returns the same array.
5168-
function appendTypeParameters(typeParameters: TypeParameter[], declarations: ReadonlyArray<TypeParameterDeclaration>): TypeParameter[] {
5168+
function appendTypeParameters(typeParameters: TypeParameter[] | undefined, declarations: ReadonlyArray<TypeParameterDeclaration>): TypeParameter[] {
51695169
for (const declaration of declarations) {
51705170
typeParameters = appendIfUnique(typeParameters, getDeclaredTypeOfTypeParameter(getSymbolOfNode(declaration)));
51715171
}
@@ -5206,7 +5206,7 @@ namespace ts {
52065206
else if (node.kind === SyntaxKind.ConditionalType) {
52075207
return concatenate(outerTypeParameters, getInferTypeParameters(<ConditionalTypeNode>node));
52085208
}
5209-
const outerAndOwnTypeParameters = appendTypeParameters(outerTypeParameters, getEffectiveTypeParameterDeclarations(<DeclarationWithTypeParameters>node) || emptyArray);
5209+
const outerAndOwnTypeParameters = appendTypeParameters(outerTypeParameters, getEffectiveTypeParameterDeclarations(<DeclarationWithTypeParameters>node));
52105210
const thisType = includeThisTypes &&
52115211
(node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.InterfaceDeclaration) &&
52125212
getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node)).thisType;
@@ -5224,17 +5224,14 @@ namespace ts {
52245224
// The local type parameters are the combined set of type parameters from all declarations of the class,
52255225
// interface, or type alias.
52265226
function getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): TypeParameter[] {
5227-
let result: TypeParameter[];
5227+
let result: TypeParameter[] | undefined;
52285228
for (const node of symbol.declarations) {
52295229
if (node.kind === SyntaxKind.InterfaceDeclaration ||
52305230
node.kind === SyntaxKind.ClassDeclaration ||
52315231
node.kind === SyntaxKind.ClassExpression ||
52325232
isTypeAlias(node)) {
52335233
const declaration = <InterfaceDeclaration | TypeAliasDeclaration | JSDocTypedefTag | JSDocCallbackTag>node;
5234-
const typeParameters = getEffectiveTypeParameterDeclarations(declaration);
5235-
if (typeParameters) {
5236-
result = appendTypeParameters(result, typeParameters);
5237-
}
5234+
result = appendTypeParameters(result, getEffectiveTypeParameterDeclarations(declaration));
52385235
}
52395236
}
52405237
return result;
@@ -5744,7 +5741,7 @@ namespace ts {
57445741
const typeParameters = getEffectiveTypeParameterDeclarations(node);
57455742
return (node.kind === SyntaxKind.Constructor || (returnType && isThislessType(returnType))) &&
57465743
node.parameters.every(isThislessVariableLikeDeclaration) &&
5747-
(!typeParameters || typeParameters.every(isThislessTypeParameter));
5744+
typeParameters.every(isThislessTypeParameter);
57485745
}
57495746

57505747
/**
@@ -7071,11 +7068,11 @@ namespace ts {
70717068

70727069
// Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual
70737070
// type checking functions).
7074-
function getTypeParametersFromDeclaration(declaration: DeclarationWithTypeParameters): TypeParameter[] {
7075-
let result: TypeParameter[];
7076-
forEach(getEffectiveTypeParameterDeclarations(declaration), node => {
7071+
function getTypeParametersFromDeclaration(declaration: DeclarationWithTypeParameters): TypeParameter[] | undefined {
7072+
let result: TypeParameter[] | undefined;
7073+
for (const node of getEffectiveTypeParameterDeclarations(declaration)) {
70777074
result = appendIfUnique(result, getDeclaredTypeOfTypeParameter(node.symbol));
7078-
});
7075+
}
70797076
return result;
70807077
}
70817078

@@ -22679,7 +22676,7 @@ namespace ts {
2267922676
// Only report errors on the last declaration for the type parameter container;
2268022677
// this ensures that all uses have been accounted for.
2268122678
const typeParameters = getEffectiveTypeParameterDeclarations(node);
22682-
if (!(node.flags & NodeFlags.Ambient) && typeParameters && last(getSymbolOfNode(node)!.declarations) === node) {
22679+
if (!(node.flags & NodeFlags.Ambient) && last(getSymbolOfNode(node)!.declarations) === node) {
2268322680
for (const typeParameter of typeParameters) {
2268422681
if (!(getMergedSymbol(typeParameter.symbol).isReferenced & SymbolFlags.TypeParameter) && !isIdentifierThatStartsWithUnderScore(typeParameter.name)) {
2268522682
addDiagnostic(UnusedKind.Parameter, createDiagnosticForNode(typeParameter.name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(typeParameter.symbol)));
@@ -24047,7 +24044,7 @@ namespace ts {
2404724044
/**
2404824045
* Check each type parameter and check that type parameters have no duplicate type parameter declarations
2404924046
*/
24050-
function checkTypeParameters(typeParameterDeclarations: ReadonlyArray<TypeParameterDeclaration>) {
24047+
function checkTypeParameters(typeParameterDeclarations: ReadonlyArray<TypeParameterDeclaration> | undefined) {
2405124048
if (typeParameterDeclarations) {
2405224049
let seenDefault = false;
2405324050
for (let i = 0; i < typeParameterDeclarations.length; i++) {
@@ -24103,7 +24100,7 @@ namespace ts {
2410324100
for (const declaration of declarations) {
2410424101
// If this declaration has too few or too many type parameters, we report an error
2410524102
const sourceParameters = getEffectiveTypeParameterDeclarations(declaration);
24106-
const numTypeParameters = length(sourceParameters);
24103+
const numTypeParameters = sourceParameters.length;
2410724104
if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) {
2410824105
return false;
2410924106
}
@@ -27371,7 +27368,7 @@ namespace ts {
2737127368
}
2737227369
}
2737327370

27374-
function checkGrammarTypeParameterList(typeParameters: NodeArray<TypeParameterDeclaration>, file: SourceFile): boolean {
27371+
function checkGrammarTypeParameterList(typeParameters: NodeArray<TypeParameterDeclaration> | undefined, file: SourceFile): boolean {
2737527372
if (typeParameters && typeParameters.length === 0) {
2737627373
const start = typeParameters.pos - "<".length;
2737727374
const end = skipTrivia(file.text, typeParameters.end) + ">".length;
@@ -27427,7 +27424,7 @@ namespace ts {
2742727424

2742827425
function checkGrammarClassLikeDeclaration(node: ClassLikeDeclaration): boolean {
2742927426
const file = getSourceFileOfNode(node);
27430-
return checkGrammarClassDeclarationHeritageClauses(node) || checkGrammarTypeParameterList(getEffectiveTypeParameterDeclarations(node), file);
27427+
return checkGrammarClassDeclarationHeritageClauses(node) || checkGrammarTypeParameterList(node.typeParameters, file);
2743127428
}
2743227429

2743327430
function checkGrammarArrowFunction(node: Node, file: SourceFile): boolean {
@@ -28199,8 +28196,8 @@ namespace ts {
2819928196

2820028197
function checkGrammarConstructorTypeParameters(node: ConstructorDeclaration) {
2820128198
const typeParameters = getEffectiveTypeParameterDeclarations(node);
28202-
if (typeParameters) {
28203-
const { pos, end } = isNodeArray(typeParameters) ? typeParameters : first(typeParameters);
28199+
if (isNodeArray(typeParameters)) {
28200+
const { pos, end } = typeParameters;
2820428201
return grammarErrorAtPos(node, pos, end - pos, Diagnostics.Type_parameters_cannot_appear_on_a_constructor_declaration);
2820528202
}
2820628203
}

src/compiler/utilities.ts

+7-10
Original file line numberDiff line numberDiff line change
@@ -3101,9 +3101,9 @@ namespace ts {
31013101
* Gets the effective type parameters. If the node was parsed in a
31023102
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
31033103
*/
3104-
export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters) {
3104+
export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration> {
31053105
if (isJSDocSignature(node)) {
3106-
return undefined;
3106+
return emptyArray;
31073107
}
31083108
if (isJSDocTypeAlias(node)) {
31093109
Debug.assert(node.parent.kind === SyntaxKind.JSDocComment);
@@ -3114,17 +3114,14 @@ namespace ts {
31143114
templateTagNodes.hasTrailingComma = false;
31153115
return templateTagNodes;
31163116
}
3117-
return node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : undefined);
3117+
return node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : emptyArray);
31183118
}
31193119

3120-
export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters) {
3120+
export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration> {
31213121
const tags = filter(getJSDocTags(node), isJSDocTemplateTag);
3122-
for (const tag of tags) {
3123-
if (!(tag.parent.kind === SyntaxKind.JSDocComment && find(tag.parent.tags, isJSDocTypeAlias))) {
3124-
// template tags are only available when a typedef isn't already using them
3125-
return tag.typeParameters;
3126-
}
3127-
}
3122+
// template tags are only available when a typedef isn't already using them
3123+
const tag = find(tags, tag => !(tag.parent.kind === SyntaxKind.JSDocComment && find(tag.parent.tags, isJSDocTypeAlias)));
3124+
return (tag && tag.typeParameters) || emptyArray;
31283125
}
31293126

31303127
/**

src/services/codefixes/annotateWithTypeFromJSDoc.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ namespace ts.codefix {
4343
if (isFunctionLikeDeclaration(decl) && (getJSDocReturnType(decl) || decl.parameters.some(p => !!getJSDocType(p)))) {
4444
if (!decl.typeParameters) {
4545
const typeParameters = getJSDocTypeParameterDeclarations(decl);
46-
if (typeParameters) changes.insertTypeParameters(sourceFile, decl, typeParameters);
46+
if (typeParameters.length) changes.insertTypeParameters(sourceFile, decl, typeParameters);
4747
}
4848
const needParens = isArrowFunction(decl) && !findChildOfKind(decl, SyntaxKind.OpenParenToken, sourceFile);
4949
if (needParens) changes.insertNodeBefore(sourceFile, first(decl.parameters), createToken(SyntaxKind.OpenParenToken));

src/services/codefixes/fixUnusedIdentifier.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,9 @@ namespace ts.codefix {
166166
case SyntaxKind.TypeParameter:
167167
const typeParameters = getEffectiveTypeParameterDeclarations(<DeclarationWithTypeParameters>parent.parent);
168168
if (typeParameters.length === 1) {
169-
const previousToken = getTokenAtPosition(sourceFile, typeParameters.pos - 1, /*includeJsDocComment*/ false);
170-
const nextToken = getTokenAtPosition(sourceFile, typeParameters.end, /*includeJsDocComment*/ false);
169+
const { pos, end } = cast(typeParameters, isNodeArray);
170+
const previousToken = getTokenAtPosition(sourceFile, pos - 1, /*includeJsDocComment*/ false);
171+
const nextToken = getTokenAtPosition(sourceFile, end, /*includeJsDocComment*/ false);
171172
Debug.assert(previousToken.kind === SyntaxKind.LessThanToken);
172173
Debug.assert(nextToken.kind === SyntaxKind.GreaterThanToken);
173174

src/services/refactors/extractSymbol.ts

+2-14
Original file line numberDiff line numberDiff line change
@@ -1464,7 +1464,7 @@ namespace ts.refactor.extractSymbol {
14641464
}
14651465

14661466
// Note that we add the current node's type parameters *after* updating the corresponding scope.
1467-
if (isDeclarationWithTypeParameters(curr) && getEffectiveTypeParameterDeclarations(curr)) {
1467+
if (isDeclarationWithTypeParameters(curr)) {
14681468
for (const typeParameterDecl of getEffectiveTypeParameterDeclarations(curr)) {
14691469
const typeParameter = checker.getTypeAtLocation(typeParameterDecl) as TypeParameter;
14701470
if (allTypeParameterUsages.has(typeParameter.id.toString())) {
@@ -1534,20 +1534,8 @@ namespace ts.refactor.extractSymbol {
15341534

15351535
return { target, usagesPerScope, functionErrorsPerScope, constantErrorsPerScope, exposedVariableDeclarations };
15361536

1537-
function hasTypeParameters(node: Node) {
1538-
return isDeclarationWithTypeParameters(node) &&
1539-
getEffectiveTypeParameterDeclarations(node) &&
1540-
getEffectiveTypeParameterDeclarations(node).length > 0;
1541-
}
1542-
15431537
function isInGenericContext(node: Node) {
1544-
for (; node; node = node.parent) {
1545-
if (hasTypeParameters(node)) {
1546-
return true;
1547-
}
1548-
}
1549-
1550-
return false;
1538+
return !!findAncestor(node, n => isDeclarationWithTypeParameters(n) && getEffectiveTypeParameterDeclarations(n).length !== 0);
15511539
}
15521540

15531541
function recordTypeParameterUsages(type: Type) {

0 commit comments

Comments
 (0)