Skip to content

Commit

Permalink
Use a mapped type to enforce type-safety on forEachChild. (#50409)
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielRosenwasser committed Aug 23, 2022
1 parent 6362fb2 commit 4605d89
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/compiler/parser.ts
Expand Up @@ -96,7 +96,8 @@ namespace ts {
}

type ForEachChildFunction<TNode> = <T>(node: TNode, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray<Node>) => T | undefined) => T | undefined;
const forEachChildTable = {
type ForEachChildTable = { [TNode in ForEachChildNodes as TNode["kind"]]: ForEachChildFunction<TNode> };
const forEachChildTable: ForEachChildTable = {
[SyntaxKind.QualifiedName]: function forEachChildInQualifiedName<T>(node: QualifiedName, cbNode: (node: Node) => T | undefined, _cbNodes?: (nodes: NodeArray<Node>) => T | undefined): T | undefined {
return visitNode(cbNode, node.left) ||
visitNode(cbNode, node.right);
Expand Down
52 changes: 51 additions & 1 deletion src/compiler/types.ts
Expand Up @@ -885,9 +885,59 @@ namespace ts {
/* @internal */ jsDocCache?: readonly JSDocTag[]; // Cache for getJSDocTags
}

// Ideally, `ForEachChildNodes` and `VisitEachChildNodes` would not differ.
// However, `forEachChild` currently processes JSDoc comment syntax and missing declarations more thoroughly.
// On the other hand, `visitEachChild` actually processes `Identifier`s (which really *shouldn't* have children,
// but are constructed as if they could for faked-up `QualifiedName`s in the language service.)

/* @internal */
export type HasChildren =
export type ForEachChildNodes =
| HasChildren
| MissingDeclaration
| JSDocTypeExpression
| JSDocNonNullableType
| JSDocNullableType
| JSDocOptionalType
| JSDocVariadicType
| JSDocFunctionType
| JSDoc
| JSDocSeeTag
| JSDocNameReference
| JSDocMemberName
| JSDocParameterTag
| JSDocPropertyTag
| JSDocAuthorTag
| JSDocImplementsTag
| JSDocAugmentsTag
| JSDocTemplateTag
| JSDocTypedefTag
| JSDocCallbackTag
| JSDocReturnTag
| JSDocTypeTag
| JSDocThisTag
| JSDocEnumTag
| JSDocSignature
| JSDocLink
| JSDocLinkCode
| JSDocLinkPlain
| JSDocTypeLiteral
| JSDocUnknownTag
| JSDocClassTag
| JSDocPublicTag
| JSDocPrivateTag
| JSDocProtectedTag
| JSDocReadonlyTag
| JSDocDeprecatedTag
;

/* @internal */
export type VisitEachChildNodes =
| HasChildren
| Identifier
;

/* @internal */
export type HasChildren =
| QualifiedName
| ComputedPropertyName
| TypeParameterDeclaration
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/visitorPublic.ts
Expand Up @@ -403,7 +403,7 @@ namespace ts {
// }
//
// This is then used as the expected type for `visitEachChildTable`.
type VisitEachChildTable = { [TNode in HasChildren as TNode["kind"]]: VisitEachChildFunction<TNode> };
type VisitEachChildTable = { [TNode in VisitEachChildNodes as TNode["kind"]]: VisitEachChildFunction<TNode> };

// NOTE: Before you can add a new method to `visitEachChildTable`, you must first ensure the `Node` subtype you
// wish to add is defined in the `HasChildren` union in types.ts.
Expand Down

0 comments on commit 4605d89

Please sign in to comment.