diff --git a/packages/babel-helper-create-class-features-plugin/src/fields.ts b/packages/babel-helper-create-class-features-plugin/src/fields.ts index 79451b0c40ee..6f814e949efb 100644 --- a/packages/babel-helper-create-class-features-plugin/src/fields.ts +++ b/packages/babel-helper-create-class-features-plugin/src/fields.ts @@ -107,7 +107,6 @@ interface PrivateNameVisitorState { function privateNameVisitorFactory( visitor: Visitor, ) { - // @ts-expect-error Fixme: TS complains _exploded: boolean does not satisfy visitor functions const privateNameVisitor: Visitor = { ...visitor, diff --git a/packages/babel-traverse/src/index.ts b/packages/babel-traverse/src/index.ts index 832aea0cdb44..03fdc6e58725 100644 --- a/packages/babel-traverse/src/index.ts +++ b/packages/babel-traverse/src/index.ts @@ -20,18 +20,11 @@ export type { HubInterface } from "./hub"; export { visitors }; -// fixme: The TraverseOptions should have been { scope ... } & Visitor -// however TS does not support excluding certain string literals from general string -// type. If we change here to { scope ... } & Visitor, TS will throw -// noScope: boolean because it matched `noScope` to the [k in string]: VisitNode<> catch-all -// in Visitor -export type TraverseOptions = - | { - scope?: Scope; - noScope?: boolean; - denylist?: string[]; - } - | Visitor; +export type TraverseOptions = { + scope?: Scope; + noScope?: boolean; + denylist?: string[]; +} & Visitor; function traverse( parent: t.Node, diff --git a/packages/babel-traverse/src/path/index.ts b/packages/babel-traverse/src/path/index.ts index 98cb5d75aa21..bcce1d642cd0 100644 --- a/packages/babel-traverse/src/path/index.ts +++ b/packages/babel-traverse/src/path/index.ts @@ -62,7 +62,7 @@ class NodePath { listKey: string | null = null; key: string | number | null = null; node: T = null; - type: string | null = null; + type: T["type"] | null = null; static get({ hub, diff --git a/packages/babel-traverse/src/path/lib/virtual-types.ts b/packages/babel-traverse/src/path/lib/virtual-types.ts index 8ae52e2bab36..f8f6c90724af 100644 --- a/packages/babel-traverse/src/path/lib/virtual-types.ts +++ b/packages/babel-traverse/src/path/lib/virtual-types.ts @@ -21,9 +21,7 @@ export interface VirtualTypeAliases { Var: t.VariableDeclaration; } -type NodeTypes = t.Node["type"] | t.Comment["type"] | keyof t.Aliases; - -type VirtualTypeMapping = readonly NodeTypes[] | null; +type VirtualTypeMapping = readonly (t.Node["type"] | keyof t.Aliases)[] | null; export const ReferencedIdentifier: VirtualTypeMapping = [ "Identifier", diff --git a/packages/babel-traverse/src/types.ts b/packages/babel-traverse/src/types.ts index a608defe7ca9..58e3353409a3 100644 --- a/packages/babel-traverse/src/types.ts +++ b/packages/babel-traverse/src/types.ts @@ -11,7 +11,10 @@ export type Visitor = VisitNodeObject & { } & { [K in keyof InternalVisitorFlags]?: InternalVisitorFlags[K]; } & { - [k: string]: VisitNode; + // Babel supports `NodeTypesWithoutComment | NodeTypesWithoutComment | ... ` but it is + // too complex for TS. So we type it as a general visitor only if the key contains `|` + // this is good enough for non-visitor traverse options e.g. `noScope` + [k: `${string}|${string}`]: VisitNode; }; /** @internal */ diff --git a/packages/babel-traverse/src/visitors.ts b/packages/babel-traverse/src/visitors.ts index 21301ccdba76..669627825cc2 100644 --- a/packages/babel-traverse/src/visitors.ts +++ b/packages/babel-traverse/src/visitors.ts @@ -28,7 +28,7 @@ export function explode(visitor: Visitor) { visitor._exploded = true; // normalise pipes - for (const nodeType of Object.keys(visitor)) { + for (const nodeType of Object.keys(visitor) as (keyof Visitor)[]) { if (shouldIgnoreKey(nodeType)) continue; const parts: Array = nodeType.split("|"); @@ -38,6 +38,7 @@ export function explode(visitor: Visitor) { delete visitor[nodeType]; for (const part of parts) { + // @ts-expect-error part will be verified by `verify` later visitor[part] = fns; } } @@ -47,6 +48,7 @@ export function explode(visitor: Visitor) { // make sure there's no __esModule type since this is because we're using loose mode // and it sets __esModule to be enumerable on all modules :( + // @ts-expect-error ESModule interop delete visitor.__esModule; // ensure visitors are objects @@ -88,12 +90,12 @@ export function explode(visitor: Visitor) { } // add aliases - for (const nodeType of Object.keys(visitor)) { + for (const nodeType of Object.keys(visitor) as (keyof Visitor)[]) { if (shouldIgnoreKey(nodeType)) continue; const fns = visitor[nodeType]; - let aliases: Array | undefined = FLIPPED_ALIAS_KEYS[nodeType]; + let aliases = FLIPPED_ALIAS_KEYS[nodeType]; const deprecatedKey = DEPRECATED_KEYS[nodeType]; if (deprecatedKey) { @@ -113,6 +115,7 @@ export function explode(visitor: Visitor) { if (existing) { mergePair(existing, fns); } else { + // @ts-expect-error Expression produces a union type that is too complex to represent. visitor[alias] = { ...fns }; } } @@ -140,7 +143,7 @@ export function verify(visitor: Visitor) { ); } - for (const nodeType of Object.keys(visitor)) { + for (const nodeType of Object.keys(visitor) as (keyof Visitor)[]) { if (nodeType === "enter" || nodeType === "exit") { validateVisitorMethods(nodeType, visitor[nodeType]); } @@ -208,7 +211,7 @@ export function merge( explode(visitor); - for (const type of Object.keys(visitor)) { + for (const type of Object.keys(visitor) as (keyof Visitor)[]) { let visitorType = visitor[type]; // if we have state or wrapper then overload the callbacks to take it @@ -216,7 +219,8 @@ export function merge( visitorType = wrapWithStateOrWrapper(visitorType, state, wrapper); } - const nodeVisitor = (rootVisitor[type] = rootVisitor[type] || {}); + // @ts-expect-error: Expression produces a union type that is too complex to represent. + const nodeVisitor = (rootVisitor[type] ||= {}); mergePair(nodeVisitor, visitorType); } } @@ -231,7 +235,7 @@ function wrapWithStateOrWrapper( ) { const newVisitor: Visitor = {}; - for (const key of Object.keys(oldVisitor)) { + for (const key of Object.keys(oldVisitor) as (keyof Visitor)[]) { let fns = oldVisitor[key]; // not an enter/exit array of callbacks @@ -260,6 +264,7 @@ function wrapWithStateOrWrapper( return newFn; }); + // @ts-expect-error: Expression produces a union type that is too complex to represent. newVisitor[key] = fns; } @@ -267,11 +272,12 @@ function wrapWithStateOrWrapper( } function ensureEntranceObjects(obj: Visitor) { - for (const key of Object.keys(obj)) { + for (const key of Object.keys(obj) as (keyof Visitor)[]) { if (shouldIgnoreKey(key)) continue; const fns = obj[key]; if (typeof fns === "function") { + // @ts-expect-error: Expression produces a union type that is too complex to represent. obj[key] = { enter: fns }; } } @@ -294,7 +300,16 @@ function wrapCheck(nodeType: VIRTUAL_TYPES, fn: Function) { return newFn; } -function shouldIgnoreKey(key: string) { +function shouldIgnoreKey( + key: string, +): key is + | "enter" + | "exit" + | "shouldSkip" + | "denylist" + | "noScope" + | "skipKeys" + | "blacklist" { // internal/hidden key if (key[0] === "_") return true; diff --git a/packages/babel-types/src/definitions/placeholders.ts b/packages/babel-types/src/definitions/placeholders.ts index e643eb0eed80..0a4b912e38da 100644 --- a/packages/babel-types/src/definitions/placeholders.ts +++ b/packages/babel-types/src/definitions/placeholders.ts @@ -9,7 +9,7 @@ export const PLACEHOLDERS = [ "BlockStatement", "ClassBody", "Pattern", -]; +] as const; export const PLACEHOLDERS_ALIAS: Record = { Declaration: ["Statement"], diff --git a/packages/babel-types/src/definitions/utils.ts b/packages/babel-types/src/definitions/utils.ts index bf2676ba99a2..dd303cea7e6e 100644 --- a/packages/babel-types/src/definitions/utils.ts +++ b/packages/babel-types/src/definitions/utils.ts @@ -3,11 +3,12 @@ import { validateField, validateChild } from "../validators/validate"; import type * as t from ".."; export const VISITOR_KEYS: Record = {}; -export const ALIAS_KEYS: Record = {}; -export const FLIPPED_ALIAS_KEYS: Record = {}; +export const ALIAS_KEYS: Partial> = + {}; +export const FLIPPED_ALIAS_KEYS: Record = {}; export const NODE_FIELDS: Record = {}; export const BUILDER_KEYS: Record = {}; -export const DEPRECATED_KEYS: Record = {}; +export const DEPRECATED_KEYS: Record = {}; export const NODE_PARENT_VALIDATIONS: Record = {}; function getType(val: any) { @@ -20,7 +21,9 @@ function getType(val: any) { } } -type NodeTypes = t.Node["type"] | t.Comment["type"] | keyof t.Aliases; +type NodeTypesWithoutComment = t.Node["type"] | keyof t.Aliases; + +type NodeTypes = NodeTypesWithoutComment | t.Comment["type"]; type PrimitiveTypes = ReturnType; @@ -330,7 +333,7 @@ export default function defineType(type: string, opts: DefineTypeOpts = {}) { } if (opts.deprecatedAlias) { - DEPRECATED_KEYS[opts.deprecatedAlias] = type; + DEPRECATED_KEYS[opts.deprecatedAlias] = type as NodeTypesWithoutComment; } // ensure all field keys are represented in `fields` @@ -360,10 +363,10 @@ export default function defineType(type: string, opts: DefineTypeOpts = {}) { VISITOR_KEYS[type] = opts.visitor = visitor; BUILDER_KEYS[type] = opts.builder = builder; NODE_FIELDS[type] = opts.fields = fields; - ALIAS_KEYS[type] = opts.aliases = aliases; + ALIAS_KEYS[type as NodeTypesWithoutComment] = opts.aliases = aliases; aliases.forEach(alias => { FLIPPED_ALIAS_KEYS[alias] = FLIPPED_ALIAS_KEYS[alias] || []; - FLIPPED_ALIAS_KEYS[alias].push(type); + FLIPPED_ALIAS_KEYS[alias].push(type as NodeTypesWithoutComment); }); if (opts.validate) { diff --git a/packages/babel-types/src/validators/isType.ts b/packages/babel-types/src/validators/isType.ts index 22f1352c2f73..b4bb627d4e99 100644 --- a/packages/babel-types/src/validators/isType.ts +++ b/packages/babel-types/src/validators/isType.ts @@ -19,6 +19,7 @@ export default function isType(nodeType: string, targetType: string): boolean { // This is a fast-path. If the test above failed, but an alias key is found, then the // targetType was a primary node type, so there's no need to check the aliases. + // @ts-expect-error targetType may not index ALIAS_KEYS if (ALIAS_KEYS[targetType]) return false; const aliases: Array | undefined = FLIPPED_ALIAS_KEYS[targetType];