Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a @nonnull JSDoc tag #57042

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,7 @@ import {
isJSDocNameReference,
isJSDocNode,
isJSDocNonNullableType,
isJSDocNonNullExpression,
isJSDocNullableType,
isJSDocOptionalParameter,
isJSDocOptionalType,
Expand Down Expand Up @@ -39397,6 +39398,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (isJSDocTypeAssertion(node)) {
return checkAssertionWorker(node, checkMode);
}
if (isJSDocNonNullExpression(node)) {
return getNonNullableType(checkExpression(node.expression));
}
}
return checkExpression(node.expression, checkMode);
}
Expand Down
1 change: 1 addition & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2170,6 +2170,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
case SyntaxKind.JSDocTag:
case SyntaxKind.JSDocClassTag:
case SyntaxKind.JSDocOverrideTag:
case SyntaxKind.JSDocNonNullTag:
return emitJSDocSimpleTag(node as JSDocTag);
case SyntaxKind.JSDocAugmentsTag:
case SyntaxKind.JSDocImplementsTag:
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ import {
JSDocNameReference,
JSDocNamespaceDeclaration,
JSDocNonNullableType,
JSDocNonNullTag,
JSDocNullableType,
JSDocOptionalType,
JSDocOverloadTag,
Expand Down Expand Up @@ -965,6 +966,12 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
get updateJSDocThrowsTag() {
return getJSDocTypeLikeTagUpdateFunction<JSDocThrowsTag>(SyntaxKind.JSDocThrowsTag);
},
get createJSDocNonNullTag() {
return getJSDocSimpleTagCreateFunction<JSDocNonNullTag>(SyntaxKind.JSDocNonNullTag);
},
get updateJSDocNonNullTag() {
return getJSDocSimpleTagUpdateFunction<JSDocNonNullTag>(SyntaxKind.JSDocNonNullTag);
},
get createJSDocSatisfiesTag() {
return getJSDocTypeLikeTagCreateFunction<JSDocSatisfiesTag>(SyntaxKind.JSDocSatisfiesTag);
},
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/factory/nodeTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ import {
JSDocNamepathType,
JSDocNameReference,
JSDocNonNullableType,
JSDocNonNullTag,
JSDocNullableType,
JSDocOptionalType,
JSDocOverloadTag,
Expand Down Expand Up @@ -1138,6 +1139,10 @@ export function isJSDocReadonlyTag(node: Node): node is JSDocReadonlyTag {
return node.kind === SyntaxKind.JSDocReadonlyTag;
}

export function isJSDocNonNullTag(node: Node): node is JSDocNonNullTag {
return node.kind === SyntaxKind.JSDocNonNullTag;
}

export function isJSDocOverrideTag(node: Node): node is JSDocOverrideTag {
return node.kind === SyntaxKind.JSDocOverrideTag;
}
Expand Down
7 changes: 6 additions & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ import {
JSDocNameReference,
JSDocNamespaceDeclaration,
JSDocNonNullableType,
JSDocNonNullTag,
JSDocNullableType,
JSDocOptionalType,
JSDocOverloadTag,
Expand Down Expand Up @@ -1125,6 +1126,7 @@ const forEachChildTable: ForEachChildTable = {
[SyntaxKind.JSDocReadonlyTag]: forEachChildInJSDocTag,
[SyntaxKind.JSDocDeprecatedTag]: forEachChildInJSDocTag,
[SyntaxKind.JSDocOverrideTag]: forEachChildInJSDocTag,
[SyntaxKind.JSDocNonNullTag]: forEachChildInJSDocTag,
[SyntaxKind.PartiallyEmittedExpression]: forEachChildInPartiallyEmittedExpression,
};

Expand Down Expand Up @@ -1209,7 +1211,7 @@ function forEachChildInJSDocLinkCodeOrPlain<T>(node: JSDocLink | JSDocLinkCode |
return visitNode(cbNode, node.name);
}

function forEachChildInJSDocTag<T>(node: JSDocUnknownTag | JSDocClassTag | JSDocPublicTag | JSDocPrivateTag | JSDocProtectedTag | JSDocReadonlyTag | JSDocDeprecatedTag | JSDocOverrideTag, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray<Node>) => T | undefined): T | undefined {
function forEachChildInJSDocTag<T>(node: JSDocUnknownTag | JSDocClassTag | JSDocPublicTag | JSDocPrivateTag | JSDocProtectedTag | JSDocReadonlyTag | JSDocDeprecatedTag | JSDocOverrideTag | JSDocNonNullTag, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray<Node>) => T | undefined): T | undefined {
return visitNode(cbNode, node.tagName)
|| (typeof node.comment === "string" ? undefined : visitNodes(cbNode, cbNodes, node.comment));
}
Expand Down Expand Up @@ -9066,6 +9068,9 @@ namespace Parser {
case "satisfies":
tag = parseSatisfiesTag(start, tagName, margin, indentText);
break;
case "nonnull":
tag = parseSimpleTag(start, factory.createJSDocNonNullTag, tagName, margin, indentText);
break;
case "see":
tag = parseSeeTag(start, tagName, margin, indentText);
break;
Expand Down
22 changes: 20 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ export const enum SyntaxKind {
JSDocPropertyTag,
JSDocThrowsTag,
JSDocSatisfiesTag,
JSDocNonNullTag,

// Synthesized list
SyntaxList,
Expand Down Expand Up @@ -489,9 +490,9 @@ export const enum SyntaxKind {
LastStatement = DebuggerStatement,
FirstNode = QualifiedName,
FirstJSDocNode = JSDocTypeExpression,
LastJSDocNode = JSDocSatisfiesTag,
LastJSDocNode = JSDocNonNullTag,
FirstJSDocTagNode = JSDocTag,
LastJSDocTagNode = JSDocSatisfiesTag,
LastJSDocTagNode = JSDocNonNullTag,
/** @internal */ FirstContextualKeyword = AbstractKeyword,
/** @internal */ LastContextualKeyword = OfKeyword,
}
Expand Down Expand Up @@ -1044,6 +1045,7 @@ export type ForEachChildNodes =
| JSDocThrowsTag
| JSDocOverrideTag
| JSDocSatisfiesTag
| JSDocNonNullTag
| JSDocOverloadTag;

/** @internal */
Expand Down Expand Up @@ -4060,11 +4062,25 @@ export interface JSDocSatisfiesTag extends JSDocTag {
readonly typeExpression: JSDocTypeExpression;
}

/**
* NOTE: this is different from {@link JSDocNonNullableType}
* which is its own kind of type node. This is a tag that acts
* like the `!` postfix operator in TypeScript.
*/
export interface JSDocNonNullTag extends JSDocTag {
readonly kind: SyntaxKind.JSDocNonNullTag;
}

/** @internal */
export interface JSDocSatisfiesExpression extends ParenthesizedExpression {
readonly _jsDocSatisfiesExpressionBrand: never;
}

/** @internal */
export interface JSDocNonNullExpression extends ParenthesizedExpression {
readonly _jsDocNonNullExpressionBrand: never;
}

// NOTE: Ensure this is up-to-date with src/debug/debug.ts
// dprint-ignore
export const enum FlowFlags {
Expand Down Expand Up @@ -8788,6 +8804,8 @@ export interface NodeFactory {
updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray<JSDocComment> | undefined): JSDocThrowsTag;
createJSDocSatisfiesTag(tagName: Identifier | undefined, typeExpression: JSDocTypeExpression, comment?: string | NodeArray<JSDocComment>): JSDocSatisfiesTag;
updateJSDocSatisfiesTag(node: JSDocSatisfiesTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression, comment: string | NodeArray<JSDocComment> | undefined): JSDocSatisfiesTag;
createJSDocNonNullTag(tagName: Identifier | undefined, comment?: string | NodeArray<JSDocComment>): JSDocNonNullTag;
updateJSDocNonNullTag(node: JSDocNonNullTag, tagName: Identifier | undefined, comment?: string | NodeArray<JSDocComment>): JSDocNonNullTag;
createJSDocText(text: string): JSDocText;
updateJSDocText(node: JSDocText, text: string): JSDocText;
createJSDocComment(comment?: string | NodeArray<JSDocComment> | undefined, tags?: readonly JSDocTag[] | undefined): JSDoc;
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ import {
getJSDocAugmentsTag,
getJSDocDeprecatedTagNoCache,
getJSDocImplementsTags,
getJSDocNonNullTag,
getJSDocOverrideTagNoCache,
getJSDocParameterTags,
getJSDocParameterTagsNoCache,
Expand Down Expand Up @@ -356,6 +357,7 @@ import {
JSDocCallbackTag,
JSDocEnumTag,
JSDocMemberName,
JSDocNonNullExpression,
JSDocOverloadTag,
JSDocParameterTag,
JSDocPropertyLikeTag,
Expand Down Expand Up @@ -10579,6 +10581,11 @@ export function tryGetJSDocSatisfiesTypeNode(node: Node) {
return tag && tag.typeExpression && tag.typeExpression.type;
}

/** @internal */
export function isJSDocNonNullExpression(node: Node): node is JSDocNonNullExpression {
return isInJSFile(node) && isParenthesizedExpression(node) && hasJSDocNodes(node) && !!getJSDocNonNullTag(node);
}

/** @internal */
export function getEscapedTextOfJsxAttributeName(node: JsxAttributeName): __String {
return isIdentifier(node) ? node.escapedText : getEscapedTextOfJsxNamespacedName(node);
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/utilitiesPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ import {
isJSDocEnumTag,
isJSDocFunctionType,
isJSDocImplementsTag,
isJSDocNonNullTag,
isJSDocOverloadTag,
isJSDocOverrideTag,
isJSDocParameterTag,
Expand Down Expand Up @@ -182,6 +183,7 @@ import {
JSDocLinkCode,
JSDocLinkPlain,
JSDocNamespaceBody,
JSDocNonNullTag,
JSDocOverrideTag,
JSDocParameterTag,
JSDocPrivateTag,
Expand Down Expand Up @@ -1093,6 +1095,11 @@ export function getJSDocReadonlyTag(node: Node): JSDocReadonlyTag | undefined {
return getFirstJSDocTag(node, isJSDocReadonlyTag);
}

/** Gets the JSDoc `nonnull` tag for the node if present */
export function getJSDocNonNullTag(node: Node): JSDocNonNullTag | undefined {
return getFirstJSDocTag(node, isJSDocNonNullTag);
}

/** @internal */
export function getJSDocReadonlyTagNoCache(node: Node): JSDocReadonlyTag | undefined {
return getFirstJSDocTag(node, isJSDocReadonlyTag, /*noCache*/ true);
Expand Down
1 change: 1 addition & 0 deletions src/services/jsDoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ const jsDocTagNames = [
"module",
"name",
"namespace",
"nonnull",
"overload",
"override",
"package",
Expand Down
30 changes: 22 additions & 8 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4534,12 +4534,13 @@ declare namespace ts {
JSDocPropertyTag = 355,
JSDocThrowsTag = 356,
JSDocSatisfiesTag = 357,
SyntaxList = 358,
NotEmittedStatement = 359,
PartiallyEmittedExpression = 360,
CommaListExpression = 361,
SyntheticReferenceExpression = 362,
Count = 363,
JSDocNonNullTag = 358,
SyntaxList = 359,
NotEmittedStatement = 360,
PartiallyEmittedExpression = 361,
CommaListExpression = 362,
SyntheticReferenceExpression = 363,
Count = 364,
FirstAssignment = 64,
LastAssignment = 79,
FirstCompoundAssignment = 65,
Expand Down Expand Up @@ -4568,9 +4569,9 @@ declare namespace ts {
LastStatement = 259,
FirstNode = 166,
FirstJSDocNode = 316,
LastJSDocNode = 357,
LastJSDocNode = 358,
FirstJSDocTagNode = 334,
LastJSDocTagNode = 357,
LastJSDocTagNode = 358,
}
type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia;
type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral;
Expand Down Expand Up @@ -6376,6 +6377,14 @@ declare namespace ts {
readonly kind: SyntaxKind.JSDocSatisfiesTag;
readonly typeExpression: JSDocTypeExpression;
}
/**
* NOTE: this is different from {@link JSDocNonNullableType}
* which is its own kind of type node. This is a tag that acts
* like the `!` postfix operator in TypeScript.
*/
interface JSDocNonNullTag extends JSDocTag {
readonly kind: SyntaxKind.JSDocNonNullTag;
}
enum FlowFlags {
Unreachable = 1,
Start = 2,
Expand Down Expand Up @@ -8320,6 +8329,8 @@ declare namespace ts {
updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray<JSDocComment> | undefined): JSDocThrowsTag;
createJSDocSatisfiesTag(tagName: Identifier | undefined, typeExpression: JSDocTypeExpression, comment?: string | NodeArray<JSDocComment>): JSDocSatisfiesTag;
updateJSDocSatisfiesTag(node: JSDocSatisfiesTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression, comment: string | NodeArray<JSDocComment> | undefined): JSDocSatisfiesTag;
createJSDocNonNullTag(tagName: Identifier | undefined, comment?: string | NodeArray<JSDocComment>): JSDocNonNullTag;
updateJSDocNonNullTag(node: JSDocNonNullTag, tagName: Identifier | undefined, comment?: string | NodeArray<JSDocComment>): JSDocNonNullTag;
createJSDocText(text: string): JSDocText;
updateJSDocText(node: JSDocText, text: string): JSDocText;
createJSDocComment(comment?: string | NodeArray<JSDocComment> | undefined, tags?: readonly JSDocTag[] | undefined): JSDoc;
Expand Down Expand Up @@ -9068,6 +9079,8 @@ declare namespace ts {
function getJSDocProtectedTag(node: Node): JSDocProtectedTag | undefined;
/** Gets the JSDoc protected tag for the node if present */
function getJSDocReadonlyTag(node: Node): JSDocReadonlyTag | undefined;
/** Gets the JSDoc `nonnull` tag for the node if present */
function getJSDocNonNullTag(node: Node): JSDocNonNullTag | undefined;
function getJSDocOverrideTagNoCache(node: Node): JSDocOverrideTag | undefined;
/** Gets the JSDoc deprecated tag for the node if present */
function getJSDocDeprecatedTag(node: Node): JSDocDeprecatedTag | undefined;
Expand Down Expand Up @@ -9528,6 +9541,7 @@ declare namespace ts {
function isJSDocPrivateTag(node: Node): node is JSDocPrivateTag;
function isJSDocProtectedTag(node: Node): node is JSDocProtectedTag;
function isJSDocReadonlyTag(node: Node): node is JSDocReadonlyTag;
function isJSDocNonNullTag(node: Node): node is JSDocNonNullTag;
function isJSDocOverrideTag(node: Node): node is JSDocOverrideTag;
function isJSDocOverloadTag(node: Node): node is JSDocOverloadTag;
function isJSDocDeprecatedTag(node: Node): node is JSDocDeprecatedTag;
Expand Down
Loading
Loading