Skip to content
Permalink
Browse files
Jsdoc property description (#50269)
* jsdocPropertyDescription

* jsdocPropertyDescription

* jsdocPropertyDescription

* Fixes #47933

* added additional test

* added additional example

* fixed bug

* changed function to only grab the literal type

* added additional condition for literals and symbols

* added additional test cases

* Update src/services/symbolDisplay.ts

Co-authored-by: Andrew Branch <andrewbranch@users.noreply.github.com>

* addressed PR review

* addressed new PR review

Co-authored-by: Danay Fernandez Alfonso <t-danayf@microsoft.com>
Co-authored-by: Andrew Branch <andrewbranch@users.noreply.github.com>
  • Loading branch information
3 people committed Aug 25, 2022
1 parent 5ba22e0 commit a08b045d2b22c87a6341a0c1d1318271d14acc86
Show file tree
Hide file tree
Showing 38 changed files with 322 additions and 5 deletions.
@@ -439,6 +439,7 @@ namespace ts {
getTypeOfPropertyOfType: (type, name) => getTypeOfPropertyOfType(type, escapeLeadingUnderscores(name)),
getIndexInfoOfType: (type, kind) => getIndexInfoOfType(type, kind === IndexKind.String ? stringType : numberType),
getIndexInfosOfType,
getIndexInfosOfIndexSymbol,
getSignaturesOfType,
getIndexTypeOfType: (type, kind) => getIndexTypeOfType(type, kind === IndexKind.String ? stringType : numberType),
getIndexType: type => getIndexType(type),
@@ -42615,6 +42616,35 @@ namespace ts {

if (name.kind === SyntaxKind.PropertyAccessExpression) {
checkPropertyAccessExpression(name, CheckMode.Normal);
if (!links.resolvedSymbol) {
const expressionType = checkExpressionCached(name.expression);
const infos = getApplicableIndexInfos(expressionType, getLiteralTypeFromPropertyName(name.name));
if (infos.length && (expressionType as ObjectType).members) {
const resolved = resolveStructuredTypeMembers(expressionType as ObjectType);
const symbol = resolved.members.get(InternalSymbolName.Index);
if (infos === getIndexInfosOfType(expressionType)) {
links.resolvedSymbol = symbol;
}
else if (symbol) {
const symbolLinks = getSymbolLinks(symbol);
const declarationList = mapDefined(infos, i => i.declaration);
const nodeListId = map(declarationList, getNodeId).join(",");
if (!symbolLinks.filteredIndexSymbolCache) {
symbolLinks.filteredIndexSymbolCache = new Map();
}
if (symbolLinks.filteredIndexSymbolCache.has(nodeListId)) {
links.resolvedSymbol = symbolLinks.filteredIndexSymbolCache.get(nodeListId)!;
}
else {
const copy = createSymbol(SymbolFlags.Signature, InternalSymbolName.Index);
copy.declarations = mapDefined(infos, i => i.declaration);
copy.parent = expressionType.aliasSymbol ? expressionType.aliasSymbol : expressionType.symbol ? expressionType.symbol : getSymbolAtLocation(copy.declarations[0].parent);
symbolLinks.filteredIndexSymbolCache.set(nodeListId, copy);
links.resolvedSymbol = symbolLinks.filteredIndexSymbolCache.get(nodeListId)!;
}
}
}
}
}
else {
checkQualifiedName(name, CheckMode.Normal);
@@ -4557,6 +4557,7 @@ namespace ts {
/* @internal */ getTypeOfPropertyOfType(type: Type, propertyName: string): Type | undefined;
getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo | undefined;
getIndexInfosOfType(type: Type): readonly IndexInfo[];
getIndexInfosOfIndexSymbol: (indexSymbol: Symbol) => IndexInfo[];
getSignaturesOfType(type: Type, kind: SignatureKind): readonly Signature[];
getIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined;
/* @internal */ getIndexType(type: Type): Type;
@@ -5373,6 +5374,7 @@ namespace ts {
isConstructorDeclaredProperty?: boolean; // Property declared through 'this.x = ...' assignment in constructor
tupleLabelDeclaration?: NamedTupleMember | ParameterDeclaration; // Declaration associated with the tuple's label
accessibleChainCache?: ESMap<string, Symbol[] | undefined>;
filteredIndexSymbolCache?: ESMap<string, Symbol> //Symbol with applicable declarations
}

/* @internal */
@@ -64,6 +64,7 @@ namespace ts.SymbolDisplay {
if (flags & SymbolFlags.SetAccessor) return ScriptElementKind.memberSetAccessorElement;
if (flags & SymbolFlags.Method) return ScriptElementKind.memberFunctionElement;
if (flags & SymbolFlags.Constructor) return ScriptElementKind.constructorImplementationElement;
if (flags & SymbolFlags.Signature) return ScriptElementKind.indexSignatureElement;

if (flags & SymbolFlags.Property) {
if (flags & SymbolFlags.Transient && (symbol as TransientSymbol).checkFlags & CheckFlags.Synthetic) {
@@ -506,19 +507,19 @@ namespace ts.SymbolDisplay {
else {
addPrefixForAnyFunctionOrVar(symbol, symbolKind);
}

// For properties, variables and local vars: show the type
if (symbolKind === ScriptElementKind.memberVariableElement ||
symbolKind === ScriptElementKind.memberGetAccessorElement ||
symbolKind === ScriptElementKind.memberSetAccessorElement ||
symbolKind === ScriptElementKind.jsxAttribute ||
symbolFlags & SymbolFlags.Variable ||
symbolKind === ScriptElementKind.localVariableElement ||
symbolKind === ScriptElementKind.indexSignatureElement ||
isThisExpression) {
displayParts.push(punctuationPart(SyntaxKind.ColonToken));
displayParts.push(spacePart());
// If the type is type parameter, format it specially
if (type.symbol && type.symbol.flags & SymbolFlags.TypeParameter) {
if (type.symbol && type.symbol.flags & SymbolFlags.TypeParameter && symbolKind !== ScriptElementKind.indexSignatureElement) {
const typeParameterParts = mapToDisplayParts(writer => {
const param = typeChecker.typeParameterToDeclaration(type as TypeParameter, enclosingDeclaration, symbolDisplayNodeBuilderFlags)!;
getPrinter().writeNode(EmitHint.Unspecified, param, getSourceFileOfNode(getParseTreeNode(enclosingDeclaration)), writer);
@@ -639,13 +640,38 @@ namespace ts.SymbolDisplay {
}

function addFullSymbolName(symbolToDisplay: Symbol, enclosingDeclaration?: Node) {
let indexInfos;

if (alias && symbolToDisplay === symbol) {
symbolToDisplay = alias;
}
const fullSymbolDisplayParts = symbolToDisplayParts(typeChecker, symbolToDisplay, enclosingDeclaration || sourceFile, /*meaning*/ undefined,
SymbolFormatFlags.WriteTypeParametersOrArguments | SymbolFormatFlags.UseOnlyExternalAliasing | SymbolFormatFlags.AllowAnyNodeKind);
addRange(displayParts, fullSymbolDisplayParts);
if (symbolKind === ScriptElementKind.indexSignatureElement) {
indexInfos = typeChecker.getIndexInfosOfIndexSymbol(symbolToDisplay);
}

let fullSymbolDisplayParts: SymbolDisplayPart[] = [];
if (symbolToDisplay.flags & SymbolFlags.Signature && indexInfos) {
if (symbolToDisplay.parent) {
fullSymbolDisplayParts = symbolToDisplayParts(typeChecker, symbolToDisplay.parent);
}
fullSymbolDisplayParts.push(punctuationPart(SyntaxKind.OpenBracketToken));
//Needed to handle more than one type of index
indexInfos.forEach((info, i) => {
//Needed to handle template literals
fullSymbolDisplayParts.push(...typeToDisplayParts(typeChecker, info.keyType));
if (i !== indexInfos.length - 1) {
fullSymbolDisplayParts.push(spacePart());
fullSymbolDisplayParts.push(punctuationPart(SyntaxKind.BarToken));
fullSymbolDisplayParts.push(spacePart());
}
});
fullSymbolDisplayParts.push(punctuationPart(SyntaxKind.CloseBracketToken));
}
else {
fullSymbolDisplayParts = symbolToDisplayParts(typeChecker, symbolToDisplay, enclosingDeclaration || sourceFile, /*meaning*/ undefined,
SymbolFormatFlags.WriteTypeParametersOrArguments | SymbolFormatFlags.UseOnlyExternalAliasing | SymbolFormatFlags.AllowAnyNodeKind);
}
addRange(displayParts, fullSymbolDisplayParts);
if (symbol.flags & SymbolFlags.Optional) {
displayParts.push(punctuationPart(SyntaxKind.QuestionToken));
}
@@ -2316,6 +2316,7 @@ declare namespace ts {
getPrivateIdentifierPropertyOfType(leftType: Type, name: string, location: Node): Symbol | undefined;
getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo | undefined;
getIndexInfosOfType(type: Type): readonly IndexInfo[];
getIndexInfosOfIndexSymbol: (indexSymbol: Symbol) => IndexInfo[];
getSignaturesOfType(type: Type, kind: SignatureKind): readonly Signature[];
getIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined;
getBaseTypes(type: InterfaceType): BaseType[];
@@ -2316,6 +2316,7 @@ declare namespace ts {
getPrivateIdentifierPropertyOfType(leftType: Type, name: string, location: Node): Symbol | undefined;
getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo | undefined;
getIndexInfosOfType(type: Type): readonly IndexInfo[];
getIndexInfosOfIndexSymbol: (indexSymbol: Symbol) => IndexInfo[];
getSignaturesOfType(type: Type, kind: SignatureKind): readonly Signature[];
getIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined;
getBaseTypes(type: InterfaceType): BaseType[];
@@ -13,7 +13,9 @@ if (typeof config['works'] !== 'boolean') {

config.works.prop = 'test'; // ok
>config.works.prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30))
>config.works : Symbol(__index, Decl(controlFlowElementAccess2.ts, 0, 23))
>config : Symbol(config, Decl(controlFlowElementAccess2.ts, 0, 13))
>works : Symbol(__index, Decl(controlFlowElementAccess2.ts, 0, 23))
>prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30))

config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string }
@@ -22,7 +24,9 @@ if (typeof config['works'] !== 'boolean') {
>prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30))
}
if (typeof config.works !== 'boolean') {
>config.works : Symbol(__index, Decl(controlFlowElementAccess2.ts, 0, 23))
>config : Symbol(config, Decl(controlFlowElementAccess2.ts, 0, 13))
>works : Symbol(__index, Decl(controlFlowElementAccess2.ts, 0, 23))

config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string }
>config['works'].prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30))
@@ -31,7 +35,9 @@ if (typeof config.works !== 'boolean') {

config.works.prop = 'test'; // ok
>config.works.prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30))
>config.works : Symbol(__index, Decl(controlFlowElementAccess2.ts, 0, 23))
>config : Symbol(config, Decl(controlFlowElementAccess2.ts, 0, 13))
>works : Symbol(__index, Decl(controlFlowElementAccess2.ts, 0, 23))
>prop : Symbol(prop, Decl(controlFlowElementAccess2.ts, 1, 30))
}

@@ -14,11 +14,15 @@ declare const value: A;
>A : Symbol(A, Decl(controlFlowStringIndex.ts, 0, 0))

if (value.foo !== null) {
>value.foo : Symbol(A.__index, Decl(controlFlowStringIndex.ts, 1, 25))
>value : Symbol(value, Decl(controlFlowStringIndex.ts, 4, 13))
>foo : Symbol(A.__index, Decl(controlFlowStringIndex.ts, 1, 25))

value.foo.toExponential()
>value.foo.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --))
>value.foo : Symbol(A.__index, Decl(controlFlowStringIndex.ts, 1, 25))
>value : Symbol(value, Decl(controlFlowStringIndex.ts, 4, 13))
>foo : Symbol(A.__index, Decl(controlFlowStringIndex.ts, 1, 25))
>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --))

value.other // should still be number | null
@@ -27,6 +31,8 @@ if (value.foo !== null) {
>other : Symbol(other, Decl(controlFlowStringIndex.ts, 0, 10))

value.bar // should still be number | null
>value.bar : Symbol(A.__index, Decl(controlFlowStringIndex.ts, 1, 25))
>value : Symbol(value, Decl(controlFlowStringIndex.ts, 4, 13))
>bar : Symbol(A.__index, Decl(controlFlowStringIndex.ts, 1, 25))
}

@@ -105,10 +105,14 @@ delete f.j
>f : Symbol(f, Decl(deleteExpressionMustBeOptional.ts, 20, 13))

delete a.a
>a.a : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional.ts, 12, 14))
>a : Symbol(a, Decl(deleteExpressionMustBeOptional.ts, 21, 13))
>a : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional.ts, 12, 14))

delete a.b
>a.b : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional.ts, 12, 14))
>a : Symbol(a, Decl(deleteExpressionMustBeOptional.ts, 21, 13))
>b : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional.ts, 12, 14))

delete b.a
>b : Symbol(b, Decl(deleteExpressionMustBeOptional.ts, 22, 13))
@@ -105,10 +105,14 @@ delete f.j
>f : Symbol(f, Decl(deleteExpressionMustBeOptional.ts, 20, 13))

delete a.a
>a.a : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional.ts, 12, 14))
>a : Symbol(a, Decl(deleteExpressionMustBeOptional.ts, 21, 13))
>a : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional.ts, 12, 14))

delete a.b
>a.b : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional.ts, 12, 14))
>a : Symbol(a, Decl(deleteExpressionMustBeOptional.ts, 21, 13))
>b : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional.ts, 12, 14))

delete b.a
>b : Symbol(b, Decl(deleteExpressionMustBeOptional.ts, 22, 13))
@@ -158,10 +158,14 @@ delete g.j
>g : Symbol(g, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 21, 13))

delete a.a
>a.a : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 12, 14))
>a : Symbol(a, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 22, 13))
>a : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 12, 14))

delete a.b
>a.b : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 12, 14))
>a : Symbol(a, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 22, 13))
>b : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 12, 14))

delete b.a
>b : Symbol(b, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 23, 13))
@@ -158,10 +158,14 @@ delete g.j
>g : Symbol(g, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 21, 13))

delete a.a
>a.a : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 12, 14))
>a : Symbol(a, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 22, 13))
>a : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 12, 14))

delete a.b
>a.b : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 12, 14))
>a : Symbol(a, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 22, 13))
>b : Symbol(AA.__index, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 12, 14))

delete b.a
>b : Symbol(b, Decl(deleteExpressionMustBeOptional_exactOptionalPropertyTypes.ts, 23, 13))
@@ -8,7 +8,9 @@

(x) => ({ "1": "one", "2": "two" } as { [key: string]: string }).x;
>x : Symbol(x, Decl(emitAccessExpressionOfCastedObjectLiteralExpressionInArrowFunctionES5.ts, 1, 1))
>({ "1": "one", "2": "two" } as { [key: string]: string }).x : Symbol(__index, Decl(emitAccessExpressionOfCastedObjectLiteralExpressionInArrowFunctionES5.ts, 1, 39))
>"1" : Symbol("1", Decl(emitAccessExpressionOfCastedObjectLiteralExpressionInArrowFunctionES5.ts, 1, 9))
>"2" : Symbol("2", Decl(emitAccessExpressionOfCastedObjectLiteralExpressionInArrowFunctionES5.ts, 1, 21))
>key : Symbol(key, Decl(emitAccessExpressionOfCastedObjectLiteralExpressionInArrowFunctionES5.ts, 1, 41))
>x : Symbol(__index, Decl(emitAccessExpressionOfCastedObjectLiteralExpressionInArrowFunctionES5.ts, 1, 39))

@@ -8,7 +8,9 @@

(x) => ({ "1": "one", "2": "two" } as { [key: string]: string }).x;
>x : Symbol(x, Decl(emitAccessExpressionOfCastedObjectLiteralExpressionInArrowFunctionES6.ts, 1, 1))
>({ "1": "one", "2": "two" } as { [key: string]: string }).x : Symbol(__index, Decl(emitAccessExpressionOfCastedObjectLiteralExpressionInArrowFunctionES6.ts, 1, 39))
>"1" : Symbol("1", Decl(emitAccessExpressionOfCastedObjectLiteralExpressionInArrowFunctionES6.ts, 1, 9))
>"2" : Symbol("2", Decl(emitAccessExpressionOfCastedObjectLiteralExpressionInArrowFunctionES6.ts, 1, 21))
>key : Symbol(key, Decl(emitAccessExpressionOfCastedObjectLiteralExpressionInArrowFunctionES6.ts, 1, 41))
>x : Symbol(__index, Decl(emitAccessExpressionOfCastedObjectLiteralExpressionInArrowFunctionES6.ts, 1, 39))

@@ -133,7 +133,9 @@ const y1 = dom['data123'];

const y2 = dom.data123;
>y2 : Symbol(y2, Decl(indexSignatures1.ts, 47, 5))
>dom.data123 : Symbol(__index, Decl(indexSignatures1.ts, 45, 18))
>dom : Symbol(dom, Decl(indexSignatures1.ts, 45, 11))
>data123 : Symbol(__index, Decl(indexSignatures1.ts, 45, 18))

// Excess property checking for template pattern index signature

@@ -90,7 +90,9 @@ function f2<T extends { [key: string]: number }>(a: { x: number, y: number }, b:
>x : Symbol(x, Decl(keyofAndIndexedAccess2.ts, 12, 53))

b.x;
>b.x : Symbol(__index, Decl(keyofAndIndexedAccess2.ts, 12, 82))
>b : Symbol(b, Decl(keyofAndIndexedAccess2.ts, 12, 77))
>x : Symbol(__index, Decl(keyofAndIndexedAccess2.ts, 12, 82))

c.x;
>c : Symbol(c, Decl(keyofAndIndexedAccess2.ts, 12, 107))
@@ -105,7 +107,9 @@ function f2<T extends { [key: string]: number }>(a: { x: number, y: number }, b:
>x : Symbol(x, Decl(keyofAndIndexedAccess2.ts, 12, 53))

b.x = 1;
>b.x : Symbol(__index, Decl(keyofAndIndexedAccess2.ts, 12, 82))
>b : Symbol(b, Decl(keyofAndIndexedAccess2.ts, 12, 77))
>x : Symbol(__index, Decl(keyofAndIndexedAccess2.ts, 12, 82))

c.x = 1; // Error, cannot write to index signature through constraint
>c : Symbol(c, Decl(keyofAndIndexedAccess2.ts, 12, 107))
@@ -51,7 +51,9 @@ a["foo"]

// access index signature
b.foo;
>b.foo : Symbol(B.__index, Decl(noPropertyAccessFromIndexSignature1.ts, 4, 13))
>b : Symbol(b, Decl(noPropertyAccessFromIndexSignature1.ts, 14, 13))
>foo : Symbol(B.__index, Decl(noPropertyAccessFromIndexSignature1.ts, 4, 13))

b["foo"];
>b : Symbol(b, Decl(noPropertyAccessFromIndexSignature1.ts, 14, 13))
@@ -68,7 +70,9 @@ c["foo"]

// access index signature
c.bar;
>c.bar : Symbol(C.__index, Decl(noPropertyAccessFromIndexSignature1.ts, 9, 15))
>c : Symbol(c, Decl(noPropertyAccessFromIndexSignature1.ts, 15, 13))
>bar : Symbol(C.__index, Decl(noPropertyAccessFromIndexSignature1.ts, 9, 15))

c["bar"];
>c : Symbol(c, Decl(noPropertyAccessFromIndexSignature1.ts, 15, 13))
@@ -85,7 +89,9 @@ d?.["foo"]

// optional access index signature
d?.bar;
>d?.bar : Symbol(C.__index, Decl(noPropertyAccessFromIndexSignature1.ts, 9, 15))
>d : Symbol(d, Decl(noPropertyAccessFromIndexSignature1.ts, 16, 13))
>bar : Symbol(C.__index, Decl(noPropertyAccessFromIndexSignature1.ts, 9, 15))

d?.["bar"];
>d : Symbol(d, Decl(noPropertyAccessFromIndexSignature1.ts, 16, 13))

0 comments on commit a08b045

Please sign in to comment.