mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-07 17:29:36 -05:00
In JS, class supports @template tag for declaring type parameters (#23511)
* Support @template as a class type parameter Still need to do the following: 1. Correctly get jsdoc host in predicate. 2. Make this work for constructor functions too. 3. Scan rest of codebase for other usages of the type parameters property that should be calls to getEffectiveTypeParameterDeclarations. 4. Rename tp to something more readable, like typar or ts'. * Use jsdoc host declaration to find container * Longer names for type parameters * Fix renaming operation * Update fourslash test * Support @template for JS constructors * Look for both outer and tag type parameters * Improve naming to improve code clarity
This commit is contained in:
committed by
GitHub
parent
84b12910e8
commit
8d969a23cb
@@ -1554,7 +1554,8 @@ namespace ts {
|
||||
|
||||
function isTypeParameterSymbolDeclaredInContainer(symbol: Symbol, container: Node) {
|
||||
for (const decl of symbol.declarations) {
|
||||
if (decl.kind === SyntaxKind.TypeParameter && decl.parent === container) {
|
||||
const parent = isJSDocTemplateTag(decl.parent) ? getJSDocHost(decl.parent) : decl.parent;
|
||||
if (decl.kind === SyntaxKind.TypeParameter && parent === container) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2060,10 +2061,10 @@ namespace ts {
|
||||
let symbol: Symbol;
|
||||
if (name.kind === SyntaxKind.Identifier) {
|
||||
const message = meaning === namespaceMeaning ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0;
|
||||
|
||||
symbol = resolveName(location || name, name.escapedText, meaning, ignoreErrors ? undefined : message, name, /*isUse*/ true);
|
||||
const symbolFromJSPrototype = isInJavaScriptFile(name) && resolveEntityNameFromJSPrototype(name, meaning);
|
||||
symbol = resolveName(location || name, name.escapedText, meaning, ignoreErrors || symbolFromJSPrototype ? undefined : message, name, /*isUse*/ true);
|
||||
if (!symbol) {
|
||||
return undefined;
|
||||
return symbolFromJSPrototype;
|
||||
}
|
||||
}
|
||||
else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
@@ -2114,6 +2115,18 @@ namespace ts {
|
||||
return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol);
|
||||
}
|
||||
|
||||
function resolveEntityNameFromJSPrototype(name: Identifier, meaning: SymbolFlags) {
|
||||
if (isJSDocTypeReference(name.parent) && isJSDocTag(name.parent.parent.parent)) {
|
||||
const host = getJSDocHost(name.parent.parent.parent as JSDocTag);
|
||||
if (isExpressionStatement(host) &&
|
||||
isBinaryExpression(host.expression) &&
|
||||
getSpecialPropertyAssignmentKind(host.expression) === SpecialPropertyAssignmentKind.PrototypeProperty) {
|
||||
const secondaryLocation = getSymbolOfNode(host.expression.left).parent.valueDeclaration;
|
||||
return resolveName(secondaryLocation, name.escapedText, meaning, /*nameNotFoundMessage*/ undefined, name, /*isUse*/ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol {
|
||||
return resolveExternalModuleNameWorker(location, moduleReferenceExpression, Diagnostics.Cannot_find_module_0);
|
||||
}
|
||||
@@ -4897,8 +4910,7 @@ namespace ts {
|
||||
// in-place and returns the same array.
|
||||
function appendTypeParameters(typeParameters: TypeParameter[], declarations: ReadonlyArray<TypeParameterDeclaration>): TypeParameter[] {
|
||||
for (const declaration of declarations) {
|
||||
const tp = getDeclaredTypeOfTypeParameter(getSymbolOfNode(declaration));
|
||||
typeParameters = appendIfUnique(typeParameters, tp);
|
||||
typeParameters = appendIfUnique(typeParameters, getDeclaredTypeOfTypeParameter(getSymbolOfNode(declaration)));
|
||||
}
|
||||
return typeParameters;
|
||||
}
|
||||
@@ -4958,8 +4970,9 @@ namespace ts {
|
||||
if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration ||
|
||||
node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.TypeAliasDeclaration) {
|
||||
const declaration = <InterfaceDeclaration | TypeAliasDeclaration>node;
|
||||
if (declaration.typeParameters) {
|
||||
result = appendTypeParameters(result, declaration.typeParameters);
|
||||
const typeParameters = getEffectiveTypeParameterDeclarations(declaration);
|
||||
if (typeParameters) {
|
||||
result = appendTypeParameters(result, typeParameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5455,9 +5468,10 @@ namespace ts {
|
||||
*/
|
||||
function isThislessFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
|
||||
const returnType = getEffectiveReturnTypeNode(node);
|
||||
const typeParameters = getEffectiveTypeParameterDeclarations(node);
|
||||
return (node.kind === SyntaxKind.Constructor || (returnType && isThislessType(returnType))) &&
|
||||
node.parameters.every(isThislessVariableLikeDeclaration) &&
|
||||
(!node.typeParameters || node.typeParameters.every(isThislessTypeParameter));
|
||||
(!typeParameters || typeParameters.every(isThislessTypeParameter));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6735,8 +6749,7 @@ namespace ts {
|
||||
function getTypeParametersFromDeclaration(declaration: DeclarationWithTypeParameters): TypeParameter[] {
|
||||
let result: TypeParameter[];
|
||||
forEach(getEffectiveTypeParameterDeclarations(declaration), node => {
|
||||
const tp = getDeclaredTypeOfTypeParameter(node.symbol);
|
||||
result = appendIfUnique(result, tp);
|
||||
result = appendIfUnique(result, getDeclaredTypeOfTypeParameter(node.symbol));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -7547,7 +7560,7 @@ namespace ts {
|
||||
return constraints ? getSubstitutionType(typeVariable, getIntersectionType(append(constraints, typeVariable))) : typeVariable;
|
||||
}
|
||||
|
||||
function isJSDocTypeReference(node: NodeWithTypeArguments): node is TypeReferenceNode {
|
||||
function isJSDocTypeReference(node: Node): node is TypeReferenceNode {
|
||||
return node.flags & NodeFlags.JSDoc && node.kind === SyntaxKind.TypeReference;
|
||||
}
|
||||
|
||||
@@ -9170,10 +9183,15 @@ namespace ts {
|
||||
// aren't the right hand side of a generic type alias declaration we optimize by reducing the
|
||||
// set of type parameters to those that are possibly referenced in the literal.
|
||||
const declaration = symbol.declarations[0];
|
||||
const outerTypeParameters = getOuterTypeParameters(declaration, /*includeThisTypes*/ true) || emptyArray;
|
||||
let outerTypeParameters = getOuterTypeParameters(declaration, /*includeThisTypes*/ true);
|
||||
if (isJavaScriptConstructor(declaration)) {
|
||||
const templateTagParameters = getTypeParametersFromDeclaration(declaration as DeclarationWithTypeParameters);
|
||||
outerTypeParameters = addRange(outerTypeParameters, templateTagParameters);
|
||||
}
|
||||
typeParameters = outerTypeParameters || emptyArray;
|
||||
typeParameters = symbol.flags & SymbolFlags.TypeLiteral && !target.aliasTypeArguments ?
|
||||
filter(outerTypeParameters, tp => isTypeParameterPossiblyReferenced(tp, declaration)) :
|
||||
outerTypeParameters;
|
||||
filter(typeParameters, tp => isTypeParameterPossiblyReferenced(tp, declaration)) :
|
||||
typeParameters;
|
||||
links.outerTypeParameters = typeParameters;
|
||||
if (typeParameters.length) {
|
||||
links.instantiations = createMap<Type>();
|
||||
@@ -18533,7 +18551,7 @@ namespace ts {
|
||||
}
|
||||
const type = funcSymbol && getJavaScriptClassType(funcSymbol);
|
||||
if (type) {
|
||||
return type;
|
||||
return signature.target ? instantiateType(type, signature.mapper) : type;
|
||||
}
|
||||
if (noImplicitAny) {
|
||||
error(node, Diagnostics.new_expression_whose_target_lacks_a_construct_signature_implicitly_has_an_any_type);
|
||||
@@ -22161,8 +22179,9 @@ namespace ts {
|
||||
): void {
|
||||
// Only report errors on the last declaration for the type parameter container;
|
||||
// this ensures that all uses have been accounted for.
|
||||
if (!(node.flags & NodeFlags.Ambient) && node.typeParameters && last(getSymbolOfNode(node)!.declarations) === node) {
|
||||
for (const typeParameter of node.typeParameters) {
|
||||
const typeParameters = getEffectiveTypeParameterDeclarations(node);
|
||||
if (!(node.flags & NodeFlags.Ambient) && typeParameters && last(getSymbolOfNode(node)!.declarations) === node) {
|
||||
for (const typeParameter of typeParameters) {
|
||||
if (!(getMergedSymbol(typeParameter.symbol).isReferenced & SymbolFlags.TypeParameter) && !isIdentifierThatStartsWithUnderScore(typeParameter.name)) {
|
||||
addDiagnostic(UnusedKind.Parameter, createDiagnosticForNode(typeParameter.name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(typeParameter.symbol)));
|
||||
}
|
||||
@@ -23536,20 +23555,21 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function areTypeParametersIdentical(declarations: ReadonlyArray<ClassDeclaration | InterfaceDeclaration>, typeParameters: TypeParameter[]) {
|
||||
const maxTypeArgumentCount = length(typeParameters);
|
||||
const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters);
|
||||
function areTypeParametersIdentical(declarations: ReadonlyArray<ClassDeclaration | InterfaceDeclaration>, targetParameters: TypeParameter[]) {
|
||||
const maxTypeArgumentCount = length(targetParameters);
|
||||
const minTypeArgumentCount = getMinTypeArgumentCount(targetParameters);
|
||||
|
||||
for (const declaration of declarations) {
|
||||
// If this declaration has too few or too many type parameters, we report an error
|
||||
const numTypeParameters = length(declaration.typeParameters);
|
||||
const sourceParameters = getEffectiveTypeParameterDeclarations(declaration);
|
||||
const numTypeParameters = length(sourceParameters);
|
||||
if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < numTypeParameters; i++) {
|
||||
const source = declaration.typeParameters[i];
|
||||
const target = typeParameters[i];
|
||||
const source = sourceParameters[i];
|
||||
const target = targetParameters[i];
|
||||
|
||||
// If the type parameter node does not have the same as the resolved type
|
||||
// parameter at this position, we report an error.
|
||||
@@ -23610,7 +23630,7 @@ namespace ts {
|
||||
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
|
||||
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
|
||||
}
|
||||
checkTypeParameters(node.typeParameters);
|
||||
checkTypeParameters(getEffectiveTypeParameterDeclarations(node));
|
||||
checkExportsOnMergedDeclarations(node);
|
||||
const symbol = getSymbolOfNode(node);
|
||||
const type = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
|
||||
@@ -26846,7 +26866,7 @@ namespace ts {
|
||||
|
||||
function checkGrammarClassLikeDeclaration(node: ClassLikeDeclaration): boolean {
|
||||
const file = getSourceFileOfNode(node);
|
||||
return checkGrammarClassDeclarationHeritageClauses(node) || checkGrammarTypeParameterList(node.typeParameters, file);
|
||||
return checkGrammarClassDeclarationHeritageClauses(node) || checkGrammarTypeParameterList(getEffectiveTypeParameterDeclarations(node), file);
|
||||
}
|
||||
|
||||
function checkGrammarArrowFunction(node: Node, file: SourceFile): boolean {
|
||||
|
||||
@@ -3055,11 +3055,11 @@ namespace ts {
|
||||
* Gets the effective type parameters. If the node was parsed in a
|
||||
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
|
||||
*/
|
||||
export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration> | undefined {
|
||||
export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters) {
|
||||
return node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : undefined);
|
||||
}
|
||||
|
||||
export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration> {
|
||||
export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters) {
|
||||
const templateTag = getJSDocTemplateTag(node);
|
||||
return templateTag && templateTag.typeParameters;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user