diff --git a/babel.config.js b/babel.config.js index f4e2a8bcadb0..474d538db65d 100644 --- a/babel.config.js +++ b/babel.config.js @@ -742,7 +742,7 @@ function pluginAddImportExtension() { } const tokenTypesMapping = new Map(); -const tokenTypeSourcePath = "./packages/babel-parser/src/tokenizer/types.js"; +const tokenTypeSourcePath = "./packages/babel-parser/src/tokenizer/types.ts"; function pluginBabelParserTokenType({ types: { isIdentifier, numericLiteral }, @@ -777,7 +777,7 @@ function pluginBabelParserTokenType({ }), { configFile: false, - parserOpts: { attachComments: false, plugins: ["flow"] }, + parserOpts: { attachComments: false, plugins: ["typescript"] }, } ); diff --git a/packages/babel-cli/src/babel/file.ts b/packages/babel-cli/src/babel/file.ts index 63222479cfbd..16bdb03fe3d7 100644 --- a/packages/babel-cli/src/babel/file.ts +++ b/packages/babel-cli/src/babel/file.ts @@ -116,7 +116,6 @@ export default async function ({ process.stdin.on("readable", function () { const chunk = process.stdin.read(); - // $FlowIgnore if (chunk !== null) code += chunk; }); diff --git a/packages/babel-cli/src/babel/options.ts b/packages/babel-cli/src/babel/options.ts index 38d35f9a07fa..98842f8e4a4a 100644 --- a/packages/babel-cli/src/babel/options.ts +++ b/packages/babel-cli/src/babel/options.ts @@ -311,7 +311,6 @@ export default function parseArgv(args: Array): CmdOptions | null { }; if (!process.env.BABEL_8_BREAKING) { - // $FlowIgnore Object.assign(babelOptions, { moduleRoot: opts.moduleRoot, moduleIds: opts.moduleIds, diff --git a/packages/babel-core/src/transformation/normalize-file.ts b/packages/babel-core/src/transformation/normalize-file.ts index ce9c0ae2df1e..6f9aa6230402 100644 --- a/packages/babel-core/src/transformation/normalize-file.ts +++ b/packages/babel-core/src/transformation/normalize-file.ts @@ -39,6 +39,7 @@ export default function* normalizeFile( ast = cloneDeep(ast) as t.File; } } else { + // @ts-expect-error todo: use babel-types ast typings in Babel parser ast = yield* parser(pluginPasses, options, code); } @@ -91,7 +92,7 @@ export default function* normalizeFile( return new File(options, { code, - ast, + ast: ast as t.File, inputMap, }); } diff --git a/packages/babel-parser/src/index.js b/packages/babel-parser/src/index.ts similarity index 74% rename from packages/babel-parser/src/index.js rename to packages/babel-parser/src/index.ts index aa93080a2d32..301dd1391e07 100644 --- a/packages/babel-parser/src/index.js +++ b/packages/babel-parser/src/index.ts @@ -1,5 +1,3 @@ -// @flow - import { type Options } from "./options"; import { hasPlugin, @@ -8,9 +6,20 @@ import { mixinPlugins, type PluginList, } from "./plugin-utils"; +import type { + PluginConfig as ParserPlugin, + FlowPluginOptions, + RecordAndTuplePluginOptions, + PipelineOperatorPluginOptions, +} from "./typings"; import Parser from "./parser"; -import { getExportedToken, tt as internalTokenTypes } from "./tokenizer/types"; +import { + ExportedTokenType, + getExportedToken, + tt as internalTokenTypes, + type InternalTokenTypes, +} from "./tokenizer/types"; import "./tokenizer/context"; import type { Expression, File } from "./types"; @@ -67,8 +76,10 @@ export function parseExpression(input: string, options?: Options): Expression { return parser.getExpression(); } -function generateExportedTokenTypes(internalTokenTypes) { - const tokenTypes = {}; +function generateExportedTokenTypes( + internalTokenTypes: InternalTokenTypes, +): Record { + const tokenTypes: Record = {}; for (const typeName of Object.keys(internalTokenTypes)) { tokenTypes[typeName] = getExportedToken(internalTokenTypes[typeName]); } @@ -77,7 +88,7 @@ function generateExportedTokenTypes(internalTokenTypes) { export const tokTypes = generateExportedTokenTypes(internalTokenTypes); -function getParser(options: ?Options, input: string): Parser { +function getParser(options: Options | undefined | null, input: string): Parser { let cls = Parser; if (options?.plugins) { validatePlugins(options.plugins); @@ -87,10 +98,12 @@ function getParser(options: ?Options, input: string): Parser { return new cls(options, input); } -const parserClassCache: { [key: string]: Class } = {}; +const parserClassCache: { [key: string]: { new (...args: any): Parser } } = {}; /** Get a Parser class with plugins applied. */ -function getParserClass(pluginsFromOptions: PluginList): Class { +function getParserClass(pluginsFromOptions: PluginList): { + new (...args: any): Parser; +} { const pluginList = mixinPluginNames.filter(name => hasPlugin(pluginsFromOptions, name), ); @@ -106,3 +119,11 @@ function getParserClass(pluginsFromOptions: PluginList): Class { } return cls; } + +export type { + FlowPluginOptions, + ParserPlugin, + PipelineOperatorPluginOptions, + RecordAndTuplePluginOptions, +}; +export type ParserOptions = Partial; diff --git a/packages/babel-parser/src/options.js b/packages/babel-parser/src/options.ts similarity index 82% rename from packages/babel-parser/src/options.js rename to packages/babel-parser/src/options.ts index e181d3da38f9..ec729544d8c3 100644 --- a/packages/babel-parser/src/options.js +++ b/packages/babel-parser/src/options.ts @@ -1,5 +1,3 @@ -// @flow - import type { PluginList } from "./plugin-utils"; // A second optional argument can be given to further configure @@ -8,22 +6,22 @@ import type { PluginList } from "./plugin-utils"; export type SourceType = "script" | "module" | "unambiguous"; export type Options = { - sourceType: SourceType, - sourceFilename?: string, - startColumn: number, - startLine: number, - allowAwaitOutsideFunction: boolean, - allowReturnOutsideFunction: boolean, - allowImportExportEverywhere: boolean, - allowSuperOutsideMethod: boolean, - allowUndeclaredExports: boolean, - plugins: PluginList, - strictMode: ?boolean, - ranges: boolean, - tokens: boolean, - createParenthesizedExpressions: boolean, - errorRecovery: boolean, - attachComment: boolean, + sourceType: SourceType; + sourceFilename?: string; + startColumn: number; + startLine: number; + allowAwaitOutsideFunction: boolean; + allowReturnOutsideFunction: boolean; + allowImportExportEverywhere: boolean; + allowSuperOutsideMethod: boolean; + allowUndeclaredExports: boolean; + plugins: PluginList; + strictMode: boolean | undefined | null; + ranges: boolean; + tokens: boolean; + createParenthesizedExpressions: boolean; + errorRecovery: boolean; + attachComment: boolean; }; export const defaultOptions: Options = { @@ -80,9 +78,10 @@ export const defaultOptions: Options = { // Interpret and default an options object -export function getOptions(opts: ?Options): Options { +export function getOptions(opts?: Options | null): Options { const options: any = {}; for (const key of Object.keys(defaultOptions)) { + // @ts-expect-error key may not exist in opts options[key] = opts && opts[key] != null ? opts[key] : defaultOptions[key]; } return options; diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.ts similarity index 76% rename from packages/babel-parser/src/parse-error.js rename to packages/babel-parser/src/parse-error.ts index 0cd9cbfc12d8..f3d13ff91b54 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.ts @@ -1,5 +1,3 @@ -// @flow - import { Position } from "./util/location"; import type { NodeBase } from "./types"; import { @@ -8,6 +6,7 @@ import { ParseErrorCodes, type ParseErrorCredentials, } from "./parse-error/credentials"; +import type { Undone } from "../src/parser/node"; // Babel uses "normal" SyntaxErrors for it's errors, but adds some extra // functionality. This functionality is defined in the @@ -25,9 +24,7 @@ interface ParseErrorSpecification { code: ParseErrorCode; reasonCode: string; syntaxPlugin?: string; - missingPlugin?: string | string[]; - loc: Position; details: ErrorDetails; @@ -44,27 +41,39 @@ export type ParseError = SyntaxError & // separate classes from `SyntaxError`'s. // // 1. https://github.com/microsoft/TypeScript/blob/v4.5.5/lib/lib.es5.d.ts#L1027 -export type ParseErrorConstructor = ({ - loc: Position, - details: ErrorDetails, +export type ParseErrorConstructor = (a: { + loc: Position; + details: ErrorDetails; }) => ParseError; -function toParseErrorConstructor({ +function toParseErrorConstructor({ toMessage, ...properties }: ParseErrorCredentials): ParseErrorConstructor { - type ConstructorArgument = { loc: Position, details: ErrorDetails }; + type ConstructorArgument = { + loc: Position; + details: ErrorDetails; + }; + return function constructor({ loc, details }: ConstructorArgument) { - return instantiate>( + return instantiate>( SyntaxError, { ...properties, loc }, { - clone(overrides: { loc?: Position, details?: ErrorDetails } = {}) { + clone( + overrides: { + loc?: Position; + details?: ErrorDetails; + } = {}, + ) { const loc = overrides.loc || {}; return constructor({ loc: new Position( + // @ts-expect-error line has been guarded "line" in loc ? loc.line : this.loc.line, + // @ts-expect-error column has been guarded "column" in loc ? loc.column : this.loc.column, + // @ts-expect-error index has been guarded "index" in loc ? loc.index : this.loc.index, ), details: { ...this.details, ...overrides.details }, @@ -72,7 +81,7 @@ function toParseErrorConstructor({ }, details: { value: details, enumerable: false }, message: { - get() { + get(this: ConstructorArgument): string { return `${toMessage(this.details)} (${this.loc.line}:${ this.loc.column })`; @@ -103,23 +112,20 @@ function toParseErrorConstructor({ // to the type system, avoiding the need to do so with $ObjMap (which doesn't // work) in `ParseErrorEnum`. This hack won't be necessary when we switch to // Typescript. -declare function toParseErrorCredentials( - T, - ?{ code?: ParseErrorCode, reasonCode?: string } | boolean, -): ParseErrorConstructor<{||}>; - -// ESLint seems to erroneously think that Flow's overloading syntax is an -// accidental redeclaration of the function: -// https://github.com/babel/eslint-plugin-babel/issues/162 -// eslint-disable-next-line no-redeclare -declare function toParseErrorCredentials( - (ErrorDetails) => string, - ?{ code?: ParseErrorCode, reasonCode?: string } | boolean, +export function toParseErrorCredentials( + message: string, + credentials?: { code?: ParseErrorCode; reasonCode?: string }, +): ParseErrorConstructor<{}>; + +export function toParseErrorCredentials( + toMessage: (details: ErrorDetails) => string, + credentials?: { code?: ParseErrorCode; reasonCode?: string }, ): ParseErrorConstructor; -// See comment about eslint and Flow overloading above. -// eslint-disable-next-line no-redeclare -export function toParseErrorCredentials(toMessageOrMessage, credentials) { +export function toParseErrorCredentials( + toMessageOrMessage: string | ((details: unknown) => string), + credentials?: any, +) { return { toMessage: typeof toMessageOrMessage === "string" @@ -130,14 +136,11 @@ export function toParseErrorCredentials(toMessageOrMessage, credentials) { } // This is the templated form. -declare function ParseErrorEnum(string[]): typeof ParseErrorEnum; +export function ParseErrorEnum(a: TemplateStringsArray): typeof ParseErrorEnum; -// See comment about eslint and Flow overloading above. -// eslint-disable-next-line no-redeclare -declare function ParseErrorEnum( - toParseErrorCredentials: (typeof toParseErrorCredentials) => T, - syntaxPlugin?: string, -): T; +export function ParseErrorEnum< + T extends (a: typeof toParseErrorCredentials) => unknown, +>(toParseErrorCredentials: T, syntaxPlugin?: string): ReturnType; // You call `ParseErrorEnum` with a mapping from `ReasonCode`'s to either error // messages, or `toMessage` functions that define additional necessary `details` @@ -148,19 +151,20 @@ declare function ParseErrorEnum( // ErrorWithDynamicMessage: _<{ type: string }>(({ type }) => `${type}`), // }); // -// See comment about eslint and Flow overloading above. -// eslint-disable-next-line no-redeclare -export function ParseErrorEnum(argument, syntaxPlugin) { +export function ParseErrorEnum(argument: any, syntaxPlugin?: string) { // If the first parameter is an array, that means we were called with a tagged // template literal. Extract the syntaxPlugin from this, and call again in // the "normalized" form. if (Array.isArray(argument)) { - return toParseErrorCredentialsMap => + return (toParseErrorCredentialsMap: any) => ParseErrorEnum(toParseErrorCredentialsMap, argument[0]); } const partialCredentials = argument(toParseErrorCredentials); - const ParseErrorConstructors = {}; + const ParseErrorConstructors = {} as Record< + string, + ParseErrorConstructor + >; for (const reasonCode of Object.keys(partialCredentials)) { ParseErrorConstructors[reasonCode] = toParseErrorConstructor({ @@ -174,10 +178,9 @@ export function ParseErrorEnum(argument, syntaxPlugin) { return ParseErrorConstructors; } -export type RaiseProperties = {| - ...ErrorDetails, - at: Position | NodeBase, -|}; +export type RaiseProperties = { + at: Position | Undone; +} & ErrorDetails; import ModuleErrors from "./parse-error/module-errors"; import StandardErrors from "./parse-error/standard-errors"; diff --git a/packages/babel-parser/src/parse-error/credentials.js b/packages/babel-parser/src/parse-error/credentials.ts similarity index 65% rename from packages/babel-parser/src/parse-error/credentials.js rename to packages/babel-parser/src/parse-error/credentials.ts index 1e758abb8731..cfca66fedfd7 100644 --- a/packages/babel-parser/src/parse-error/credentials.js +++ b/packages/babel-parser/src/parse-error/credentials.ts @@ -1,11 +1,10 @@ -// @flow - export const ParseErrorCodes = Object.freeze({ SyntaxError: "BABEL_PARSER_SYNTAX_ERROR", SourceTypeModuleError: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED", }); -export type ParseErrorCode = $Values; +export type ParseErrorCode = + typeof ParseErrorCodes[keyof typeof ParseErrorCodes]; export type SyntaxPlugin = | "flow" @@ -17,29 +16,34 @@ export type SyntaxPlugin = export type ToMessage = (self: ErrorDetails) => string; export type ParseErrorCredentials = { - code: ParseErrorCode, - reasonCode: string, - syntaxPlugin?: SyntaxPlugin, - - toMessage: ToMessage, + code: ParseErrorCode; + reasonCode: string; + syntaxPlugin?: SyntaxPlugin; + toMessage: ToMessage; }; const reflect = (keys: string[], last = keys.length - 1) => ({ - get() { - return keys.reduce((object, key) => object[key], this); + get(this: unknown): unknown { + return keys.reduce( + (object, key) => + // @ts-expect-error key should index object + object[key], + this, + ); }, - set(value) { + set(this: unknown, value: unknown) { keys.reduce( + // @ts-expect-error key should index item (item, key, i) => (i === last ? (item[key] = value) : item[key]), this, ); }, }); -const instantiate = ( - constructor: () => any, - properties: Object, - descriptors: Object, +const instantiate = ( + constructor: new () => T, + properties: any, + descriptors: any, ) => Object.keys(descriptors) .map(key => [key, descriptors[key]]) @@ -58,7 +62,7 @@ const instantiate = ( configurable: true, ...descriptor, }), - Object.assign((new constructor(): T), properties), + Object.assign(new constructor() as U, properties), ); export { instantiate }; diff --git a/packages/babel-parser/src/parse-error/module-errors.js b/packages/babel-parser/src/parse-error/module-errors.ts similarity index 97% rename from packages/babel-parser/src/parse-error/module-errors.js rename to packages/babel-parser/src/parse-error/module-errors.ts index 82f83c222731..465d8018b572 100644 --- a/packages/babel-parser/src/parse-error/module-errors.js +++ b/packages/babel-parser/src/parse-error/module-errors.ts @@ -1,5 +1,3 @@ -// @flow - import { ParseErrorCodes, toParseErrorCredentials } from "../parse-error"; export default (_: typeof toParseErrorCredentials) => ({ diff --git a/packages/babel-parser/src/parse-error/pipeline-operator-errors.js b/packages/babel-parser/src/parse-error/pipeline-operator-errors.ts similarity index 87% rename from packages/babel-parser/src/parse-error/pipeline-operator-errors.js rename to packages/babel-parser/src/parse-error/pipeline-operator-errors.ts index a5a66d6d5eb7..26aaca6f4659 100644 --- a/packages/babel-parser/src/parse-error/pipeline-operator-errors.js +++ b/packages/babel-parser/src/parse-error/pipeline-operator-errors.ts @@ -1,14 +1,15 @@ -// @flow - import { toParseErrorCredentials } from "../parse-error"; import toNodeDescription from "./to-node-description"; -export const UnparenthesizedPipeBodyDescriptions = new Set([ +const UnparenthesizedPipeBodyDescriptionsList = [ "ArrowFunctionExpression", "AssignmentExpression", "ConditionalExpression", "YieldExpression", -]); +] as const; +export const UnparenthesizedPipeBodyDescriptions = new Set( + UnparenthesizedPipeBodyDescriptionsList, +); export default (_: typeof toParseErrorCredentials) => ({ // This error is only used by the smart-mix proposal @@ -21,14 +22,16 @@ export default (_: typeof toParseErrorCredentials) => ({ PipeTopicUnbound: _( "Topic reference is unbound; it must be inside a pipe body.", ), - PipeTopicUnconfiguredToken: _<{| token: string |}>( + PipeTopicUnconfiguredToken: _<{ token: string }>( ({ token }) => `Invalid topic token ${token}. In order to use ${token} as a topic reference, the pipelineOperator plugin must be configured with { "proposal": "hack", "topicToken": "${token}" }.`, ), PipeTopicUnused: _( "Hack-style pipe body does not contain a topic reference; Hack-style pipes must use topic at least once.", ), - PipeUnparenthesizedBody: _<{| type: string |}>( + PipeUnparenthesizedBody: _<{ + type: typeof UnparenthesizedPipeBodyDescriptionsList[number]; + }>( ({ type }) => `Hack-style pipe body cannot be an unparenthesized ${toNodeDescription({ type, diff --git a/packages/babel-parser/src/parse-error/standard-errors.js b/packages/babel-parser/src/parse-error/standard-errors.ts similarity index 88% rename from packages/babel-parser/src/parse-error/standard-errors.js rename to packages/babel-parser/src/parse-error/standard-errors.ts index 3f9747ce1d38..1dae5fcec9e8 100644 --- a/packages/babel-parser/src/parse-error/standard-errors.js +++ b/packages/babel-parser/src/parse-error/standard-errors.ts @@ -1,10 +1,8 @@ -// @flow - import { toParseErrorCredentials } from "../parse-error"; import toNodeDescription from "./to-node-description"; export type LValAncestor = - | { type: "UpdateExpression", prefix: boolean } + | { type: "UpdateExpression"; prefix: boolean } | { type: | "ArrayPattern" @@ -14,14 +12,16 @@ export type LValAncestor = | "FormalParameters" | "ForInStatement" | "ForStatement" - | "Identfier" + | "ImportSpecifier" + | "ImportNamespaceSpecifier" + | "ImportDefaultSpecifier" | "ObjectPattern" | "RestElement" - | "VariableDeclarator", + | "VariableDeclarator"; }; export default (_: typeof toParseErrorCredentials) => ({ - AccessorIsGenerator: _<{| kind: "get" | "set" |}>( + AccessorIsGenerator: _<{ kind: "get" | "set" }>( ({ kind }) => `A ${kind}ter cannot be a generator.`, ), @@ -56,7 +56,7 @@ export default (_: typeof toParseErrorCredentials) => ({ ConstructorIsAccessor: _("Class constructor may not be an accessor."), ConstructorIsAsync: _("Constructor can't be an async function."), ConstructorIsGenerator: _("Constructor can't be a generator."), - DeclarationMissingInitializer: _<{| kind: "const" | "destructuring" |}>( + DeclarationMissingInitializer: _<{ kind: "const" | "destructuring" }>( ({ kind }) => `Missing initializer in ${kind} declaration.`, ), DecoratorBeforeExport: _( @@ -76,7 +76,7 @@ export default (_: typeof toParseErrorCredentials) => ({ ), DuplicateConstructor: _("Duplicate constructor in the same class."), DuplicateDefaultExport: _("Only one default export allowed per module."), - DuplicateExport: _<{| exportName: string |}>( + DuplicateExport: _<{ exportName: string }>( ({ exportName }) => `\`${exportName}\` has already been exported. Exported identifiers must be unique.`, ), @@ -84,7 +84,7 @@ export default (_: typeof toParseErrorCredentials) => ({ DuplicateRegExpFlags: _("Duplicate regular expression flag."), ElementAfterRest: _("Rest element must be last element."), EscapedCharNotAnIdentifier: _("Invalid Unicode escape."), - ExportBindingIsString: _<{| localName: string, exportName: string |}>( + ExportBindingIsString: _<{ localName: string; exportName: string }>( ({ localName, exportName }) => `A string literal cannot be used as an exported binding without \`from\`.\n- Did you mean \`export { '${localName}' as '${exportName}' } from 'some-module'\`?`, ), @@ -92,7 +92,7 @@ export default (_: typeof toParseErrorCredentials) => ({ "'from' is not allowed as an identifier after 'export default'.", ), - ForInOfLoopInitializer: _<{| type: "ForInStatement" | "ForOfStatement" |}>( + ForInOfLoopInitializer: _<{ type: "ForInStatement" | "ForOfStatement" }>( ({ type }) => `'${ type === "ForInStatement" ? "for-in" : "for-of" @@ -105,7 +105,7 @@ export default (_: typeof toParseErrorCredentials) => ({ "Generators can only be declared at the top level or inside a block.", ), - IllegalBreakContinue: _<{| type: "BreakStatement" | "ContinueStatement" |}>( + IllegalBreakContinue: _<{ type: "BreakStatement" | "ContinueStatement" }>( ({ type }) => `Unsyntactic ${type === "BreakStatement" ? "break" : "continue"}.`, ), @@ -114,14 +114,14 @@ export default (_: typeof toParseErrorCredentials) => ({ "Illegal 'use strict' directive in function with non-simple parameter list.", ), IllegalReturn: _("'return' outside of function."), - ImportBindingIsString: _<{| importName: string |}>( + ImportBindingIsString: _<{ importName: string }>( ({ importName }) => `A string literal cannot be used as an imported binding.\n- Did you mean \`import { "${importName}" as foo }\`?`, ), ImportCallArgumentTrailingComma: _( "Trailing comma is disallowed inside import(...) arguments.", ), - ImportCallArity: _<{| maxArgumentCount: 1 | 2 |}>( + ImportCallArity: _<{ maxArgumentCount: 1 | 2 }>( ({ maxArgumentCount }) => `\`import()\` requires exactly ${ maxArgumentCount === 1 ? "one argument" : "one or two arguments" @@ -139,22 +139,22 @@ export default (_: typeof toParseErrorCredentials) => ({ InvalidCodePoint: _("Code point out of bounds."), InvalidCoverInitializedName: _("Invalid shorthand property initializer."), InvalidDecimal: _("Invalid decimal."), - InvalidDigit: _<{| radix: number |}>( + InvalidDigit: _<{ radix: number }>( ({ radix }) => `Expected number in radix ${radix}.`, ), InvalidEscapeSequence: _("Bad character escape sequence."), InvalidEscapeSequenceTemplate: _("Invalid escape sequence in template."), - InvalidEscapedReservedWord: _<{| reservedWord: string |}>( + InvalidEscapedReservedWord: _<{ reservedWord: string }>( ({ reservedWord }) => `Escape sequence in keyword ${reservedWord}.`, ), - InvalidIdentifier: _<{| identifierName: string |}>( + InvalidIdentifier: _<{ identifierName: string }>( ({ identifierName }) => `Invalid identifier ${identifierName}.`, ), - InvalidLhs: _<{| ancestor: LValAncestor |}>( + InvalidLhs: _<{ ancestor: LValAncestor }>( ({ ancestor }) => `Invalid left-hand side in ${toNodeDescription(ancestor)}.`, ), - InvalidLhsBinding: _<{| ancestor: LValAncestor |}>( + InvalidLhsBinding: _<{ ancestor: LValAncestor }>( ({ ancestor }) => `Binding invalid left-hand side in ${toNodeDescription(ancestor)}.`, ), @@ -162,13 +162,13 @@ export default (_: typeof toParseErrorCredentials) => ({ InvalidOrMissingExponent: _( "Floating-point numbers require a valid exponent after the 'e'.", ), - InvalidOrUnexpectedToken: _<{| unexpected: string |}>( + InvalidOrUnexpectedToken: _<{ unexpected: string }>( ({ unexpected }) => `Unexpected character '${unexpected}'.`, ), InvalidParenthesizedAssignment: _( "Invalid parenthesized assignment pattern.", ), - InvalidPrivateFieldResolution: _<{| identifierName: string |}>( + InvalidPrivateFieldResolution: _<{ identifierName: string }>( ({ identifierName }) => `Private name #${identifierName} is not defined.`, ), InvalidPropertyBindingPattern: _("Binding member expression."), @@ -176,7 +176,7 @@ export default (_: typeof toParseErrorCredentials) => ({ "Only properties and spread elements are allowed in record definitions.", ), InvalidRestAssignmentPattern: _("Invalid rest operator's argument."), - LabelRedeclaration: _<{| labelName: string |}>( + LabelRedeclaration: _<{ labelName: string }>( ({ labelName }) => `Label '${labelName}' is already declared.`, ), LetInLexicalBinding: _( @@ -189,7 +189,7 @@ export default (_: typeof toParseErrorCredentials) => ({ "Only '=' operator can be used for specifying default value.", ), MissingSemicolon: _("Missing semicolon."), - MissingPlugin: _<{| missingPlugin: [string] |}>( + MissingPlugin: _<{ missingPlugin: [string] }>( ({ missingPlugin }) => `This experimental syntax requires enabling the parser plugin: ${missingPlugin .map(name => JSON.stringify(name)) @@ -197,7 +197,7 @@ export default (_: typeof toParseErrorCredentials) => ({ ), // FIXME: Would be nice to make this "missingPlugins" instead. // Also), seems like we can drop the "(s)" from the message and just make it "s". - MissingOneOfPlugins: _<{| missingPlugin: string[] |}>( + MissingOneOfPlugins: _<{ missingPlugin: string[] }>( ({ missingPlugin }) => `This experimental syntax requires enabling one of the following parser plugin(s): ${missingPlugin .map(name => JSON.stringify(name)) @@ -213,16 +213,16 @@ export default (_: typeof toParseErrorCredentials) => ({ ModuleAttributeInvalidValue: _( "Only string literals are allowed as module attribute values.", ), - ModuleAttributesWithDuplicateKeys: _<{| key: string |}>( + ModuleAttributesWithDuplicateKeys: _<{ key: string }>( ({ key }) => `Duplicate key "${key}" is not allowed in module attributes.`, ), - ModuleExportNameHasLoneSurrogate: _<{| surrogateCharCode: number |}>( + ModuleExportNameHasLoneSurrogate: _<{ surrogateCharCode: number }>( ({ surrogateCharCode }) => `An export name cannot include a lone surrogate, found '\\u${surrogateCharCode.toString( 16, )}'.`, ), - ModuleExportUndefined: _<{| localName: string |}>( + ModuleExportUndefined: _<{ localName: string }>( ({ localName }) => `Export '${localName}' is not defined.`, ), MultipleDefaultsInSwitch: _("Multiple default clauses."), @@ -247,11 +247,11 @@ export default (_: typeof toParseErrorCredentials) => ({ ParamDupe: _("Argument name clash."), PatternHasAccessor: _("Object pattern can't contain getter or setter."), PatternHasMethod: _("Object pattern can't contain methods."), - PrivateInExpectedIn: _<{| identifierName: string |}>( + PrivateInExpectedIn: _<{ identifierName: string }>( ({ identifierName }) => `Private names are only allowed in property accesses (\`obj.#${identifierName}\`) or in \`in\` expressions (\`#${identifierName} in obj\`).`, ), - PrivateNameRedeclaration: _<{| identifierName: string |}>( + PrivateNameRedeclaration: _<{ identifierName: string }>( ({ identifierName }) => `Duplicate private name #${identifierName}.`, ), RecordExpressionBarIncorrectEndSyntaxType: _( @@ -291,7 +291,7 @@ export default (_: typeof toParseErrorCredentials) => ({ UnexpectedImportExport: _( "'import' and 'export' may only appear at the top level.", ), - UnexpectedKeyword: _<{| keyword: string |}>( + UnexpectedKeyword: _<{ keyword: string }>( ({ keyword }) => `Unexpected keyword '${keyword}'.`, ), UnexpectedLeadingDecorator: _( @@ -307,14 +307,11 @@ export default (_: typeof toParseErrorCredentials) => ({ "A numeric separator is only allowed between two digits.", ), UnexpectedPrivateField: _("Unexpected private name."), - UnexpectedReservedWord: _<{| reservedWord: string |}>( + UnexpectedReservedWord: _<{ reservedWord: string }>( ({ reservedWord }) => `Unexpected reserved word '${reservedWord}'.`, ), UnexpectedSuper: _("'super' is only allowed in object methods and classes."), - UnexpectedToken: _<{| - expected?: ?string, - unexpected?: ?string, - |}>( + UnexpectedToken: _<{ expected?: string | null; unexpected?: string | null }>( ({ expected, unexpected }) => `Unexpected token${unexpected ? ` '${unexpected}'.` : ""}${ expected ? `, expected "${expected}"` : "" @@ -333,10 +330,7 @@ export default (_: typeof toParseErrorCredentials) => ({ UnsupportedImport: _( "`import` can only be used in `import()` or `import.meta`.", ), - UnsupportedMetaProperty: _<{| - target: string, - onlyValidPropertyName: string, - |}>( + UnsupportedMetaProperty: _<{ target: string; onlyValidPropertyName: string }>( ({ target, onlyValidPropertyName }) => `The only valid meta property for ${target} is ${target}.${onlyValidPropertyName}.`, ), @@ -353,7 +347,7 @@ export default (_: typeof toParseErrorCredentials) => ({ UnterminatedRegExp: _("Unterminated regular expression."), UnterminatedString: _("Unterminated string constant."), UnterminatedTemplate: _("Unterminated template."), - VarRedeclaration: _<{| identifierName: string |}>( + VarRedeclaration: _<{ identifierName: string }>( ({ identifierName }) => `Identifier '${identifierName}' has already been declared.`, ), diff --git a/packages/babel-parser/src/parse-error/strict-mode-errors.js b/packages/babel-parser/src/parse-error/strict-mode-errors.ts similarity index 81% rename from packages/babel-parser/src/parse-error/strict-mode-errors.js rename to packages/babel-parser/src/parse-error/strict-mode-errors.ts index 62d4fe9038f2..1a6da37ac225 100644 --- a/packages/babel-parser/src/parse-error/strict-mode-errors.js +++ b/packages/babel-parser/src/parse-error/strict-mode-errors.ts @@ -1,5 +1,3 @@ -// @flow - import { toParseErrorCredentials } from "../parse-error"; export default (_: typeof toParseErrorCredentials) => ({ @@ -10,17 +8,17 @@ export default (_: typeof toParseErrorCredentials) => ({ // 1. https://tc39.es/ecma262/#sec-static-semantics-stringvalue // 2. https://tc39.es/ecma262/#prod-IdentifierReference // 3. https://github.com/babel/babel/blob/main/packages/babel-parser/ast/spec.md#identifier - StrictEvalArguments: _<{| referenceName: string |}>( - ({ referenceName }) => `Assigning to '${referenceName}' in strict mode.`, - ), + StrictEvalArguments: _<{ + referenceName: string; + }>(({ referenceName }) => `Assigning to '${referenceName}' in strict mode.`), // `bindingName` is the StringValue[1] of a BindingIdentifier[2], which is // represented as just an `Identifier`[3] in the Babel AST. // 1. https://tc39.es/ecma262/#sec-static-semantics-stringvalue // 2. https://tc39.es/ecma262/#prod-BindingIdentifier // 3. https://github.com/babel/babel/blob/main/packages/babel-parser/ast/spec.md#identifier - StrictEvalArgumentsBinding: _<{| bindingName: string |}>( - ({ bindingName }) => `Binding '${bindingName}' in strict mode.`, - ), + StrictEvalArgumentsBinding: _<{ + bindingName: string; + }>(({ bindingName }) => `Binding '${bindingName}' in strict mode.`), StrictFunction: _( "In strict mode code, functions can only be declared at top level or inside a block.", diff --git a/packages/babel-parser/src/parse-error/to-node-description.js b/packages/babel-parser/src/parse-error/to-node-description.ts similarity index 64% rename from packages/babel-parser/src/parse-error/to-node-description.js rename to packages/babel-parser/src/parse-error/to-node-description.ts index c604e30454b1..6fa5e816a490 100644 --- a/packages/babel-parser/src/parse-error/to-node-description.js +++ b/packages/babel-parser/src/parse-error/to-node-description.ts @@ -4,11 +4,15 @@ const NodeDescriptions = { AssignmentPattern: "assignment expression", ArrowFunctionExpression: "arrow function expression", ConditionalExpression: "conditional expression", + CatchClause: "catch clause", ForOfStatement: "for-of statement", ForInStatement: "for-in statement", ForStatement: "for-loop", FormalParameters: "function parameter list", Identifier: "identifier", + ImportSpecifier: "import specifier", + ImportDefaultSpecifier: "import default specifier", + ImportNamespaceSpecifier: "import namespace specifier", ObjectPattern: "object destructuring pattern", ParenthesizedExpression: "parenthesized expression", RestElement: "rest element", @@ -20,17 +24,25 @@ const NodeDescriptions = { YieldExpression: "yield expression", }; -type NodeTypesWithDescriptions = $Keys< - $Diff, +type NodeTypesWithDescriptions = keyof Omit< + typeof NodeDescriptions, + "UpdateExpression" >; + type NodeWithDescription = - | { type: "UpdateExpression", prefix: boolean } - | { type: NodeTypesWithDescriptions }; + | { + type: "UpdateExpression"; + prefix: boolean; + } + | { + type: NodeTypesWithDescriptions; + }; +// @ts-expect-error prefix is specified only when type is UpdateExpression // eslint-disable-next-line no-confusing-arrow const toNodeDescription = ({ type, prefix }: NodeWithDescription) => type === "UpdateExpression" - ? NodeDescriptions.UpdateExpression[String(prefix)] + ? NodeDescriptions.UpdateExpression[String(prefix) as "true" | "false"] : NodeDescriptions[type]; export default toNodeDescription; diff --git a/packages/babel-parser/src/parser/base.js b/packages/babel-parser/src/parser/base.ts similarity index 89% rename from packages/babel-parser/src/parser/base.js rename to packages/babel-parser/src/parser/base.ts index 522534ea5fff..5158a34f2a0a 100644 --- a/packages/babel-parser/src/parser/base.js +++ b/packages/babel-parser/src/parser/base.ts @@ -1,5 +1,3 @@ -// @flow - import type { Options } from "../options"; import type State from "../tokenizer/state"; import type { PluginsMap } from "./index"; @@ -7,17 +5,18 @@ import type ScopeHandler from "../util/scope"; import type ExpressionScopeHandler from "../util/expression-scope"; import type ClassScopeHandler from "../util/class-scope"; import type ProductionParameterHandler from "../util/production-parameter"; +import type { PluginConfig } from "../typings"; export default class BaseParser { // Properties set by constructor in index.js declare options: Options; declare inModule: boolean; - declare scope: ScopeHandler<*>; + declare scope: ScopeHandler; declare classScope: ClassScopeHandler; declare prodParam: ProductionParameterHandler; declare expressionScope: ExpressionScopeHandler; declare plugins: PluginsMap; - declare filename: ?string; + declare filename: string | undefined | null; // Names of exports store. `default` is stored as a name for both // `export default foo;` and `export { foo as default };`. declare exportedIdentifiers: Set; @@ -44,7 +43,9 @@ export default class BaseParser { return false; } const actualOptions = this.plugins.get(pluginName); - for (const key of Object.keys(pluginOptions)) { + for (const key of Object.keys( + pluginOptions, + ) as (keyof typeof pluginOptions)[]) { if (actualOptions?.[key] !== pluginOptions[key]) { return false; } @@ -57,5 +58,3 @@ export default class BaseParser { return this.plugins.get(plugin)?.[name]; } } - -export type PluginConfig = string | [string, { [string]: any }]; diff --git a/packages/babel-parser/src/parser/comments.js b/packages/babel-parser/src/parser/comments.ts similarity index 98% rename from packages/babel-parser/src/parser/comments.js rename to packages/babel-parser/src/parser/comments.ts index 9b50e63356c3..9258aaeafc11 100644 --- a/packages/babel-parser/src/parser/comments.js +++ b/packages/babel-parser/src/parser/comments.ts @@ -1,5 +1,3 @@ -// @flow - /*:: declare var invariant; */ import BaseParser from "./base"; @@ -19,12 +17,12 @@ import * as charCodes from "charcodes"; * with minimal size (|end - start|) */ export type CommentWhitespace = { - start: number, - end: number, - comments: Array, - leadingNode: Node | null, - trailingNode: Node | null, - containingNode: Node | null, + start: number; + end: number; + comments: Array; + leadingNode: Node | null; + trailingNode: Node | null; + containingNode: Node | null; }; /** diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.ts similarity index 86% rename from packages/babel-parser/src/parser/expression.js rename to packages/babel-parser/src/parser/expression.ts index 3b968ffc90e0..9d3569e45ed9 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.ts @@ -1,5 +1,3 @@ -// @flow - // A recursive descent parser operates by defining functions for all // syntactic elements, and recursively calling those, each function // advancing the input stream and returning an AST node. Precedence @@ -70,7 +68,8 @@ import { import { Errors, type ParseError } from "../parse-error"; import { UnparenthesizedPipeBodyDescriptions } from "../parse-error/pipeline-operator-errors"; import { setInnerComments } from "./comments"; -import { cloneIdentifier } from "./node"; +import { cloneIdentifier, type Undone } from "./node"; +import type Parser from "."; /*:: import type { SourceType } from "../options"; @@ -119,15 +118,17 @@ export default class ExpressionParser extends LValParser { checkProto( prop: N.ObjectMember | N.SpreadElement, - isRecord: ?boolean, - protoRef: { used: boolean }, - refExpressionErrors: ?ExpressionErrors, + isRecord: boolean | undefined | null, + protoRef: { + used: boolean; + }, + refExpressionErrors?: ExpressionErrors | null, ): void { if ( prop.type === "SpreadElement" || this.isObjectMethod(prop) || prop.computed || - // $FlowIgnore + // @ts-expect-error prop.shorthand ) { return; @@ -165,7 +166,7 @@ export default class ExpressionParser extends LValParser { } // Convenience method to parse an Expression only - getExpression(): N.Expression & N.ParserOutput { + getExpression(this: Parser): N.Expression & N.ParserOutput { this.enterInitialScopes(); this.nextToken(); const expr = this.parseExpression(); @@ -180,6 +181,7 @@ export default class ExpressionParser extends LValParser { if (this.options.tokens) { expr.tokens = this.tokens; } + // @ts-expect-error fixme: refine types return expr; } @@ -203,6 +205,7 @@ export default class ExpressionParser extends LValParser { // delayed syntax error at correct position). parseExpression( + this: Parser, disallowIn?: boolean, refExpressionErrors?: ExpressionErrors, ): N.Expression { @@ -215,7 +218,10 @@ export default class ExpressionParser extends LValParser { } // https://tc39.es/ecma262/#prod-Expression - parseExpressionBase(refExpressionErrors?: ExpressionErrors): N.Expression { + parseExpressionBase( + this: Parser, + refExpressionErrors?: ExpressionErrors, + ): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; const expr = this.parseMaybeAssign(refExpressionErrors); @@ -233,7 +239,8 @@ export default class ExpressionParser extends LValParser { // Set [~In] parameter for assignment expression parseMaybeAssignDisallowIn( - refExpressionErrors?: ?ExpressionErrors, + this: Parser, + refExpressionErrors?: ExpressionErrors | null, afterLeftParse?: Function, ) { return this.disallowInAnd(() => @@ -243,7 +250,8 @@ export default class ExpressionParser extends LValParser { // Set [+In] parameter for assignment expression parseMaybeAssignAllowIn( - refExpressionErrors?: ?ExpressionErrors, + this: Parser, + refExpressionErrors?: ExpressionErrors | null, afterLeftParse?: Function, ) { return this.allowInAnd(() => @@ -265,7 +273,8 @@ export default class ExpressionParser extends LValParser { // operators like `+=`. // https://tc39.es/ecma262/#prod-AssignmentExpression parseMaybeAssign( - refExpressionErrors?: ?ExpressionErrors, + this: Parser, + refExpressionErrors?: ExpressionErrors | null, afterLeftParse?: Function, ): N.Expression { const startPos = this.state.start; @@ -298,7 +307,7 @@ export default class ExpressionParser extends LValParser { left = afterLeftParse.call(this, left, startPos, startLoc); } if (tokenIsAssignment(this.state.type)) { - const node = this.startNodeAt(startPos, startLoc); + const node = this.startNodeAt(startPos, startLoc); const operator = this.state.value; node.operator = operator; @@ -345,7 +354,10 @@ export default class ExpressionParser extends LValParser { // Parse a ternary conditional (`?:`) operator. // https://tc39.es/ecma262/#prod-ConditionalExpression - parseMaybeConditional(refExpressionErrors: ExpressionErrors): N.Expression { + parseMaybeConditional( + this: Parser, + refExpressionErrors: ExpressionErrors, + ): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; const potentialArrowAt = this.state.potentialArrowAt; @@ -359,11 +371,12 @@ export default class ExpressionParser extends LValParser { } parseConditional( + this: Parser, expr: N.Expression, startPos: number, startLoc: Position, - // eslint-disable-next-line no-unused-vars - refExpressionErrors?: ?ExpressionErrors, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + refExpressionErrors?: ExpressionErrors | null, ): N.Expression { if (this.eat(tt.question)) { const node = this.startNodeAt(startPos, startLoc); @@ -377,6 +390,7 @@ export default class ExpressionParser extends LValParser { } parseMaybeUnaryOrPrivate( + this: Parser, refExpressionErrors?: ExpressionErrors, ): N.Expression | N.PrivateName { return this.match(tt.privateName) @@ -387,7 +401,10 @@ export default class ExpressionParser extends LValParser { // Start the precedence parser. // https://tc39.es/ecma262/#prod-ShortCircuitExpression - parseExprOps(refExpressionErrors: ExpressionErrors): N.Expression { + parseExprOps( + this: Parser, + refExpressionErrors: ExpressionErrors, + ): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; const potentialArrowAt = this.state.potentialArrowAt; @@ -407,6 +424,7 @@ export default class ExpressionParser extends LValParser { // operator that has a lower precedence than the set it is parsing. parseExprOp( + this: Parser, left: N.Expression | N.PrivateName, leftStartPos: number, leftStartLoc: Position, @@ -444,7 +462,10 @@ export default class ExpressionParser extends LValParser { } this.checkPipelineAtInfixOperator(left, leftStartLoc); } - const node = this.startNodeAt(leftStartPos, leftStartLoc); + const node = this.startNodeAt( + leftStartPos, + leftStartLoc, + ); node.left = left; node.operator = this.state.value; @@ -471,7 +492,7 @@ export default class ExpressionParser extends LValParser { } node.right = this.parseExprOpRightExpr(op, prec); - this.finishNode( + const finishedNode = this.finishNode( node, logical || coalesce ? "LogicalExpression" : "BinaryExpression", ); @@ -490,7 +511,12 @@ export default class ExpressionParser extends LValParser { }); } - return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec); + return this.parseExprOp( + finishedNode, + leftStartPos, + leftStartLoc, + minPrec, + ); } } return left; @@ -499,7 +525,11 @@ export default class ExpressionParser extends LValParser { // Helper function for `parseExprOp`. Parse the right-hand side of binary- // operator expressions, then apply any operator-specific functions. - parseExprOpRightExpr(op: TokenType, prec: number): N.Expression { + parseExprOpRightExpr( + this: Parser, + op: TokenType, + prec: number, + ): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; switch (op) { @@ -539,7 +569,11 @@ export default class ExpressionParser extends LValParser { // Helper function for `parseExprOpRightExpr`. Parse the right-hand side of // binary-operator expressions without applying any operator-specific functions. - parseExprOpBaseRightExpr(op: TokenType, prec: number): N.Expression { + parseExprOpBaseRightExpr( + this: Parser, + op: TokenType, + prec: number, + ): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; @@ -551,7 +585,7 @@ export default class ExpressionParser extends LValParser { ); } - parseHackPipeBody(): N.Expression { + parseHackPipeBody(this: Parser): N.Expression { const { startLoc } = this.state; const body = this.parseMaybeAssign(); const requiredParentheses = UnparenthesizedPipeBodyDescriptions.has( @@ -573,7 +607,9 @@ export default class ExpressionParser extends LValParser { return body; } - checkExponentialAfterUnary(node: N.AwaitExpression | N.UnaryExpression) { + checkExponentialAfterUnary( + node: N.AwaitExpression | Undone, + ) { if (this.match(tt.exponent)) { this.raise(Errors.UnexpectedTokenUnaryExponentiation, { at: node.argument, @@ -584,7 +620,8 @@ export default class ExpressionParser extends LValParser { // Parse unary operators, both prefix and postfix. // https://tc39.es/ecma262/#prod-UnaryExpression parseMaybeUnary( - refExpressionErrors: ?ExpressionErrors, + this: Parser, + refExpressionErrors?: ExpressionErrors | null, sawUnary?: boolean, ): N.Expression { const startPos = this.state.start; @@ -598,7 +635,7 @@ export default class ExpressionParser extends LValParser { return expr; } const update = this.match(tt.incDec); - const node = this.startNode(); + const node = this.startNode(); if (tokenIsPrefix(this.state.type)) { node.operator = this.state.value; node.prefix = true; @@ -624,7 +661,9 @@ export default class ExpressionParser extends LValParser { } if (!update) { - if (!sawUnary) this.checkExponentialAfterUnary(node); + if (!sawUnary) { + this.checkExponentialAfterUnary(node as Undone); + } return this.finishNode(node, "UnaryExpression"); } } @@ -647,13 +686,17 @@ export default class ExpressionParser extends LValParser { // https://tc39.es/ecma262/#prod-UpdateExpression parseUpdate( + this: Parser, node: N.Expression, update: boolean, - refExpressionErrors: ?ExpressionErrors, + refExpressionErrors?: ExpressionErrors | null, ): N.Expression { if (update) { - this.checkLVal(node.argument, { - in: this.finishNode(node, "UpdateExpression"), + this.checkLVal((node as Undone).argument, { + in: this.finishNode( + node as Undone, + "UpdateExpression", + ), }); return node; } @@ -663,7 +706,7 @@ export default class ExpressionParser extends LValParser { let expr = this.parseExprSubscripts(refExpressionErrors); if (this.checkExpressionErrors(refExpressionErrors, false)) return expr; while (tokenIsPostfix(this.state.type) && !this.canInsertSemicolon()) { - const node = this.startNodeAt(startPos, startLoc); + const node = this.startNodeAt(startPos, startLoc); node.operator = this.state.value; node.prefix = false; node.argument = expr; @@ -677,7 +720,10 @@ export default class ExpressionParser extends LValParser { // Parse call, dot, and `[]`-subscript expressions. // https://tc39.es/ecma262/#prod-LeftHandSideExpression - parseExprSubscripts(refExpressionErrors: ?ExpressionErrors): N.Expression { + parseExprSubscripts( + this: Parser, + refExpressionErrors?: ExpressionErrors | null, + ): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; const potentialArrowAt = this.state.potentialArrowAt; @@ -691,10 +737,11 @@ export default class ExpressionParser extends LValParser { } parseSubscripts( + this: Parser, base: N.Expression, startPos: number, startLoc: Position, - noCalls?: ?boolean, + noCalls?: boolean | null, ): N.Expression { const state = { optionalChainMember: false, @@ -715,10 +762,11 @@ export default class ExpressionParser extends LValParser { * state.optionalChainMember to indicate that the member is currently in OptionalChain */ parseSubscript( + this: Parser, base: N.Expression, startPos: number, startLoc: Position, - noCalls: ?boolean, + noCalls: boolean | undefined | null, state: N.ParseSubscriptState, ): N.Expression { const { type } = this.state; @@ -776,6 +824,7 @@ export default class ExpressionParser extends LValParser { // base[?Yield, ?Await] . PrivateIdentifier // where `base` is one of CallExpression, MemberExpression and OptionalChain parseMember( + this: Parser, base: N.Expression, startPos: number, startLoc: Position, @@ -783,7 +832,9 @@ export default class ExpressionParser extends LValParser { computed: boolean, optional: boolean, ): N.OptionalMemberExpression | N.MemberExpression { - const node = this.startNodeAt(startPos, startLoc); + const node = this.startNodeAt< + N.OptionalMemberExpression | N.MemberExpression + >(startPos, startLoc); node.object = base; node.computed = computed; if (computed) { @@ -800,7 +851,7 @@ export default class ExpressionParser extends LValParser { } if (state.optionalChainMember) { - node.optional = optional; + (node as N.OptionalMemberExpression).optional = optional; return this.finishNode(node, "OptionalMemberExpression"); } else { return this.finishNode(node, "MemberExpression"); @@ -809,10 +860,11 @@ export default class ExpressionParser extends LValParser { // https://github.com/tc39/proposal-bind-operator#syntax parseBind( + this: Parser, base: N.Expression, startPos: number, startLoc: Position, - noCalls: ?boolean, + noCalls: boolean | undefined | null, state: N.ParseSubscriptState, ): N.Expression { const node = this.startNodeAt(startPos, startLoc); @@ -833,6 +885,7 @@ export default class ExpressionParser extends LValParser { // CallExpression[?Yield, ?Await] Arguments[?Yield, ?Await] // OptionalChain[?Yield, ?Await] Arguments[?Yield, ?Await] parseCoverCallAndAsyncArrowHead( + this: Parser, base: N.Expression, startPos: number, startLoc: Position, @@ -840,12 +893,15 @@ export default class ExpressionParser extends LValParser { optional: boolean, ): N.Expression { const oldMaybeInArrowParameters = this.state.maybeInArrowParameters; - let refExpressionErrors = null; + let refExpressionErrors: ExpressionErrors | null = null; this.state.maybeInArrowParameters = true; this.next(); // eat `(` - let node = this.startNodeAt(startPos, startLoc); + const node = this.startNodeAt( + startPos, + startLoc, + ); node.callee = base; const { maybeAsyncArrow, optionalChainMember } = state; @@ -855,6 +911,7 @@ export default class ExpressionParser extends LValParser { } if (optionalChainMember) { + // @ts-expect-error when optionalChainMember is true, node must be an optional call node.optional = optional; } @@ -869,7 +926,13 @@ export default class ExpressionParser extends LValParser { refExpressionErrors, ); } - this.finishCallExpression(node, optionalChainMember); + let finishedNode: + | N.CallExpression + | N.OptionalCallExpression + | N.ArrowFunctionExpression = this.finishCallExpression( + node, + optionalChainMember, + ); if (maybeAsyncArrow && this.shouldParseAsyncArrow() && !optional) { /*:: invariant(refExpressionErrors != null) */ @@ -877,21 +940,21 @@ export default class ExpressionParser extends LValParser { this.checkDestructuringPrivate(refExpressionErrors); this.expressionScope.validateAsPattern(); this.expressionScope.exit(); - node = this.parseAsyncArrowFromCallExpression( - this.startNodeAt(startPos, startLoc), - node, + finishedNode = this.parseAsyncArrowFromCallExpression( + this.startNodeAt(startPos, startLoc), + finishedNode as N.CallExpression, ); } else { if (maybeAsyncArrow) { this.checkExpressionErrors(refExpressionErrors, true); this.expressionScope.exit(); } - this.toReferencedArguments(node); + this.toReferencedArguments(finishedNode); } this.state.maybeInArrowParameters = oldMaybeInArrowParameters; - return node; + return finishedNode; } toReferencedArguments( @@ -904,12 +967,13 @@ export default class ExpressionParser extends LValParser { // MemberExpression [?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged] // CallExpression [?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged] parseTaggedTemplateExpression( + this: Parser, base: N.Expression, startPos: number, startLoc: Position, state: N.ParseSubscriptState, ): N.TaggedTemplateExpression { - const node: N.TaggedTemplateExpression = this.startNodeAt( + const node = this.startNodeAt( startPos, startLoc, ); @@ -933,10 +997,10 @@ export default class ExpressionParser extends LValParser { ); } - finishCallExpression( - node: T, + finishCallExpression( + node: Undone, optional: boolean, - ): N.Expression { + ): T { if (node.callee.type === "Import") { if (node.arguments.length === 2) { if (process.env.BABEL_8_BREAKING) { @@ -971,13 +1035,14 @@ export default class ExpressionParser extends LValParser { } parseCallExpressionArguments( + this: Parser, close: TokenType, dynamicImport?: boolean, allowPlaceholder?: boolean, - nodeForExtra?: ?N.Node, - refExpressionErrors?: ?ExpressionErrors, - ): $ReadOnlyArray { - const elts = []; + nodeForExtra?: N.Node | null, + refExpressionErrors?: ExpressionErrors | null, + ): Array { + const elts: N.Expression[] = []; let first = true; const oldInFSharpPipelineDirectBody = this.state.inFSharpPipelineDirectBody; this.state.inFSharpPipelineDirectBody = false; @@ -1020,7 +1085,8 @@ export default class ExpressionParser extends LValParser { } parseAsyncArrowFromCallExpression( - node: N.ArrowFunctionExpression, + this: Parser, + node: Undone, call: N.CallExpression, ): N.ArrowFunctionExpression { this.resetPreviousNodeTrailingComments(call); @@ -1039,12 +1105,12 @@ export default class ExpressionParser extends LValParser { if (call.callee.trailingComments) { setInnerComments(node, call.callee.trailingComments); } - return node; + return node as N.ArrowFunctionExpression; } // Parse a no-call expression (like argument of `new` or `::` operators). // https://tc39.es/ecma262/#prod-MemberExpression - parseNoCallExpr(): N.Expression { + parseNoCallExpr(this: Parser): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; return this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true); @@ -1062,7 +1128,10 @@ export default class ExpressionParser extends LValParser { // Import // AsyncArrowFunction - parseExprAtom(refExpressionErrors?: ?ExpressionErrors): N.Expression { + parseExprAtom( + this: Parser, + refExpressionErrors?: ExpressionErrors | null, + ): N.Expression { let node; const { type } = this.state; @@ -1071,11 +1140,11 @@ export default class ExpressionParser extends LValParser { return this.parseSuper(); case tt._import: - node = this.startNode(); + node = this.startNode(); this.next(); if (this.match(tt.dot)) { - return this.parseImportMetaProperty(node); + return this.parseImportMetaProperty(node as Undone); } if (!this.match(tt.parenL)) { @@ -1163,7 +1232,7 @@ export default class ExpressionParser extends LValParser { this.parseDecorators(); // fall through case tt._class: - node = this.startNode(); + node = this.startNode(); this.takeDecorators(node); return this.parseClass(node, false); @@ -1356,7 +1425,7 @@ export default class ExpressionParser extends LValParser { // its configuration might not match the current token’s type. // See . parseTopicReference(pipeProposal: string): N.Expression { - const node = this.startNode(); + const node = this.startNode(); const startLoc = this.state.startLoc; const tokenType = this.state.type; @@ -1449,6 +1518,7 @@ export default class ExpressionParser extends LValParser { return this.hasPlugin([ "pipelineOperator", { + // @ts-expect-error token must have a label topicToken: tokenLabelName(tokenType), }, ]); @@ -1461,7 +1531,10 @@ export default class ExpressionParser extends LValParser { } // async [no LineTerminator here] AsyncArrowBindingIdentifier[?Yield] [no LineTerminator here] => AsyncConciseBody[?In] - parseAsyncArrowUnaryFunction(node: N.Node): N.ArrowFunctionExpression { + parseAsyncArrowUnaryFunction( + this: Parser, + node: Undone, + ): N.ArrowFunctionExpression { // We don't need to push a new ParameterDeclarationScope here since we are sure // 1) it is an async arrow, 2) no biding pattern is allowed in params this.prodParam.enter(functionFlags(true, this.prodParam.hasYield)); @@ -1474,13 +1547,16 @@ export default class ExpressionParser extends LValParser { } this.expect(tt.arrow); // let foo = async bar => {}; - this.parseArrowExpression(node, params, true); - return node; + return this.parseArrowExpression(node, params, true); } // https://github.com/tc39/proposal-do-expressions // https://github.com/tc39/proposal-async-do-expressions - parseDo(node: N.Node, isAsync: boolean): N.DoExpression { + parseDo( + this: Parser, + node: Undone, + isAsync: boolean, + ): N.DoExpression { this.expectPlugin("doExpressions"); if (isAsync) { this.expectPlugin("asyncDoExpressions"); @@ -1505,7 +1581,7 @@ export default class ExpressionParser extends LValParser { // Parse the `super` keyword parseSuper(): N.Super { - const node = this.startNode(); + const node = this.startNode(); this.next(); // eat `super` if ( this.match(tt.parenL) && @@ -1532,8 +1608,8 @@ export default class ExpressionParser extends LValParser { } parsePrivateName(): N.PrivateName { - const node = this.startNode(); - const id = this.startNodeAt( + const node = this.startNode(); + const id = this.startNodeAt( this.state.start + 1, // The position is hardcoded because we merge `#` and name into a single // tt.privateName token @@ -1549,8 +1625,10 @@ export default class ExpressionParser extends LValParser { return this.finishNode(node, "PrivateName"); } - parseFunctionOrFunctionSent(): N.FunctionExpression | N.MetaProperty { - const node = this.startNode(); + parseFunctionOrFunctionSent( + this: Parser, + ): N.FunctionExpression | N.MetaProperty { + const node = this.startNode(); // We do not do parseIdentifier here because when parseFunctionOrFunctionSent // is called we already know that the current token is a "name" with the value "function" @@ -1561,7 +1639,7 @@ export default class ExpressionParser extends LValParser { if (this.prodParam.hasYield && this.match(tt.dot)) { const meta = this.createIdentifier( - this.startNodeAtNode(node), + this.startNodeAtNode(node), "function", ); this.next(); // eat `.` @@ -1574,11 +1652,11 @@ export default class ExpressionParser extends LValParser { } return this.parseMetaProperty(node, meta, "sent"); } - return this.parseFunction(node); + return this.parseFunction(node as Undone); } parseMetaProperty( - node: N.MetaProperty, + node: Undone, meta: N.Identifier, propertyName: string, ): N.MetaProperty { @@ -1600,8 +1678,11 @@ export default class ExpressionParser extends LValParser { } // https://tc39.es/ecma262/#prod-ImportMeta - parseImportMetaProperty(node: N.MetaProperty): N.MetaProperty { - const id = this.createIdentifier(this.startNodeAtNode(node), "import"); + parseImportMetaProperty(node: Undone): N.MetaProperty { + const id = this.createIdentifier( + this.startNodeAtNode(node), + "import", + ); this.next(); // eat `.` if (this.isContextual(tt._meta)) { @@ -1614,9 +1695,9 @@ export default class ExpressionParser extends LValParser { return this.parseMetaProperty(node, id, "meta"); } - parseLiteralAtNode( + parseLiteralAtNode( value: any, - type: $ElementType, + type: T["type"], node: any, ): T { this.addExtra(node, "rawValue", value); @@ -1626,7 +1707,7 @@ export default class ExpressionParser extends LValParser { return this.finishNode(node, type); } - parseLiteral(value: any, type: $ElementType): T { + parseLiteral(value: any, type: T["type"]): T { const node = this.startNode(); return this.parseLiteralAtNode(value, type, node); } @@ -1647,7 +1728,11 @@ export default class ExpressionParser extends LValParser { return this.parseLiteral(value, "DecimalLiteral"); } - parseRegExpLiteral(value: { value: any, pattern: string, flags: string }) { + parseRegExpLiteral(value: { + value: any; + pattern: string; + flags: N.RegExpLiteral["flags"]; + }) { const node = this.parseLiteral( value.value, "RegExpLiteral", @@ -1658,20 +1743,23 @@ export default class ExpressionParser extends LValParser { } parseBooleanLiteral(value: boolean) { - const node = this.startNode(); + const node = this.startNode(); node.value = value; this.next(); - return this.finishNode(node, "BooleanLiteral"); + return this.finishNode(node, "BooleanLiteral"); } parseNullLiteral() { - const node = this.startNode(); + const node = this.startNode(); this.next(); - return this.finishNode(node, "NullLiteral"); + return this.finishNode(node, "NullLiteral"); } // https://tc39.es/ecma262/#prod-CoverParenthesizedExpressionAndArrowParameterList - parseParenAndDistinguishExpression(canBeArrow: boolean): N.Expression { + parseParenAndDistinguishExpression( + this: Parser, + canBeArrow: boolean, + ): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; @@ -1686,7 +1774,7 @@ export default class ExpressionParser extends LValParser { const innerStartPos = this.state.start; const innerStartLoc = this.state.startLoc; - const exprList = []; + const exprList: N.Expression[] = []; const refExpressionErrors = new ExpressionErrors(); let first = true; let spreadStartLoc; @@ -1739,7 +1827,10 @@ export default class ExpressionParser extends LValParser { this.state.maybeInArrowParameters = oldMaybeInArrowParameters; this.state.inFSharpPipelineDirectBody = oldInFSharpPipelineDirectBody; - let arrowNode = this.startNodeAt(startPos, startLoc); + let arrowNode = this.startNodeAt( + startPos, + startLoc, + ); if ( canBeArrow && this.shouldParseArrow(exprList) && @@ -1762,7 +1853,10 @@ export default class ExpressionParser extends LValParser { this.toReferencedListDeep(exprList, /* isParenthesizedExpr */ true); if (exprList.length > 1) { - val = this.startNodeAt(innerStartPos, innerStartLoc); + val = this.startNodeAt( + innerStartPos, + innerStartLoc, + ); val.expressions = exprList; // finish node at current location so it can pick up comments after `)` this.finishNode(val, "SequenceExpression"); @@ -1792,18 +1886,22 @@ export default class ExpressionParser extends LValParser { return expression; } - const parenExpression = this.startNodeAt(startPos, startLoc); + const parenExpression = this.startNodeAt( + startPos, + startLoc, + ); parenExpression.expression = expression; - this.finishNode(parenExpression, "ParenthesizedExpression"); - return parenExpression; + return this.finishNode(parenExpression, "ParenthesizedExpression"); } - // eslint-disable-next-line no-unused-vars -- `params` is used in typescript plugin + // eslint-disable-next-line @typescript-eslint/no-unused-vars -- `params` is used in typescript plugin shouldParseArrow(params: Array): boolean { return !this.canInsertSemicolon(); } - parseArrow(node: N.ArrowFunctionExpression): ?N.ArrowFunctionExpression { + parseArrow( + node: Undone, + ): Undone | undefined { if (this.eat(tt.arrow)) { return node; } @@ -1811,20 +1909,29 @@ export default class ExpressionParser extends LValParser { parseParenItem( node: N.Expression, - startPos: number, // eslint-disable-line no-unused-vars - startLoc: Position, // eslint-disable-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars + startPos: number, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + startLoc: Position, ): N.Expression { return node; } - parseNewOrNewTarget(): N.NewExpression | N.MetaProperty { - const node = this.startNode(); + parseNewOrNewTarget(this: Parser): N.NewExpression | N.MetaProperty { + const node = this.startNode(); this.next(); if (this.match(tt.dot)) { // https://tc39.es/ecma262/#prod-NewTarget - const meta = this.createIdentifier(this.startNodeAtNode(node), "new"); + const meta = this.createIdentifier( + this.startNodeAtNode(node), + "new", + ); this.next(); - const metaProp = this.parseMetaProperty(node, meta, "target"); + const metaProp = this.parseMetaProperty( + node as Undone, + meta, + "target", + ); if (!this.scope.inNonArrowFunction && !this.scope.inClass) { this.raise(Errors.UnexpectedNewTarget, { at: metaProp }); @@ -1833,7 +1940,7 @@ export default class ExpressionParser extends LValParser { return metaProp; } - return this.parseNew(node); + return this.parseNew(node as Undone); } // New's precedence is slightly tricky. It must allow its argument to @@ -1842,13 +1949,13 @@ export default class ExpressionParser extends LValParser { // argument to parseSubscripts to prevent it from consuming the // argument list. // https://tc39.es/ecma262/#prod-NewExpression - parseNew(node: N.NewExpression): N.NewExpression { + parseNew(this: Parser, node: Undone): N.NewExpression { this.parseNewCallee(node); if (this.eat(tt.parenL)) { const args = this.parseExprList(tt.parenR); this.toReferencedList(args); - // $FlowFixMe (parseExprList should be all non-null in this case) + // (parseExprList should be all non-null in this case) node.arguments = args; } else { node.arguments = []; @@ -1857,7 +1964,7 @@ export default class ExpressionParser extends LValParser { return this.finishNode(node, "NewExpression"); } - parseNewCallee(node: N.NewExpression): void { + parseNewCallee(this: Parser, node: Undone): void { node.callee = this.parseNoCallExpr(); if (node.callee.type === "Import") { this.raise(Errors.ImportCallNotNewExpression, { at: node.callee }); @@ -1877,7 +1984,7 @@ export default class ExpressionParser extends LValParser { parseTemplateElement(isTagged: boolean): N.TemplateElement { const { start, startLoc, end, value } = this.state; const elemStart = start + 1; - const elem = this.startNodeAt( + const elem = this.startNodeAt( elemStart, createPositionWithColumnOffset(startLoc, 1), ); @@ -1899,17 +2006,17 @@ export default class ExpressionParser extends LValParser { }; elem.tail = isTail; this.next(); - this.finishNode(elem, "TemplateElement"); + const finishedNode = this.finishNode(elem, "TemplateElement"); this.resetEndLocation( - elem, + finishedNode, createPositionWithColumnOffset(this.state.lastTokEndLoc, endOffset), ); - return elem; + return finishedNode; } // https://tc39.es/ecma262/#prod-TemplateLiteral - parseTemplate(isTagged: boolean): N.TemplateLiteral { - const node = this.startNode(); + parseTemplate(this: Parser, isTagged: boolean): N.TemplateLiteral { + const node = this.startNode(); node.expressions = []; let curElt = this.parseTemplateElement(isTagged); node.quasis = [curElt]; @@ -1922,17 +2029,36 @@ export default class ExpressionParser extends LValParser { } // This is overwritten by the TypeScript plugin to parse template types - parseTemplateSubstitution(): N.Expression { + parseTemplateSubstitution(this: Parser): N.Expression { return this.parseExpression(); } // Parse an object literal, binding pattern, or record. - parseObjectLike( + parseObjectLike( + close: TokenType, + isPattern: true, + isRecord?: boolean | null, + refExpressionErrors?: ExpressionErrors | null, + ): N.ObjectPattern; + parseObjectLike( + close: TokenType, + isPattern: false, + isRecord?: false | null, + refExpressionErrors?: ExpressionErrors | null, + ): N.ObjectExpression; + parseObjectLike( + close: TokenType, + isPattern: false, + isRecord?: true, + refExpressionErrors?: ExpressionErrors | null, + ): N.RecordExpression; + parseObjectLike( + this: Parser, close: TokenType, isPattern: boolean, - isRecord?: ?boolean, - refExpressionErrors?: ?ExpressionErrors, + isRecord?: boolean | null, + refExpressionErrors?: ExpressionErrors | null, ): T { if (isRecord) { this.expectPlugin("recordAndTuple"); @@ -1941,7 +2067,9 @@ export default class ExpressionParser extends LValParser { this.state.inFSharpPipelineDirectBody = false; const propHash: any = Object.create(null); let first = true; - const node = this.startNode(); + const node = this.startNode< + N.ObjectExpression | N.ObjectPattern | N.RecordExpression + >(); node.properties = []; this.next(); @@ -1973,11 +2101,12 @@ export default class ExpressionParser extends LValParser { this.raise(Errors.InvalidRecordProperty, { at: prop }); } - // $FlowIgnore + // @ts-expect-error if (prop.shorthand) { this.addExtra(prop, "shorthand", true); } + // @ts-expect-error Fixme: refine typings node.properties.push(prop); } @@ -1990,6 +2119,7 @@ export default class ExpressionParser extends LValParser { } else if (isRecord) { type = "RecordExpression"; } + // @ts-expect-error type is well defined return this.finishNode(node, type); } @@ -2001,7 +2131,7 @@ export default class ExpressionParser extends LValParser { // Check grammar production: // IdentifierName *_opt PropertyName // It is used in `parsePropertyDefinition` to detect AsyncMethod and Accessors - maybeAsyncOrAccessorProp(prop: N.ObjectProperty): boolean { + maybeAsyncOrAccessorProp(prop: Undone): boolean { return ( !prop.computed && prop.key.type === "Identifier" && @@ -2013,7 +2143,8 @@ export default class ExpressionParser extends LValParser { // https://tc39.es/ecma262/#prod-PropertyDefinition parsePropertyDefinition( - refExpressionErrors?: ?ExpressionErrors, + this: Parser, + refExpressionErrors?: ExpressionErrors | null, ): N.ObjectMember | N.SpreadElement { let decorators = []; if (this.match(tt.at)) { @@ -2030,7 +2161,7 @@ export default class ExpressionParser extends LValParser { } } - const prop = this.startNode(); + const prop = this.startNode(); let isAsync = false; let isAccessor = false; let startPos; @@ -2086,7 +2217,7 @@ export default class ExpressionParser extends LValParser { } } - this.parseObjPropValue( + return this.parseObjPropValue( prop, startPos, startLoc, @@ -2096,8 +2227,6 @@ export default class ExpressionParser extends LValParser { isAccessor, refExpressionErrors, ); - - return prop; } getGetterSetterExpectedParamCount( @@ -2134,15 +2263,16 @@ export default class ExpressionParser extends LValParser { // https://tc39.es/ecma262/#prod-MethodDefinition parseObjectMethod( - prop: N.ObjectMethod, + this: Parser, + prop: Undone, isGenerator: boolean, isAsync: boolean, isPattern: boolean, isAccessor: boolean, - ): ?N.ObjectMethod { + ): N.ObjectMethod | undefined | null { if (isAccessor) { // isAccessor implies isAsync: false, isPattern: false, isGenerator: false - this.parseMethod( + const finishedProp = this.parseMethod( prop, // This _should_ be false, but with error recovery, we allow it to be // set for informational purposes @@ -2152,8 +2282,8 @@ export default class ExpressionParser extends LValParser { false, "ObjectMethod", ); - this.checkGetterSetterParams(prop); - return prop; + this.checkGetterSetterParams(finishedProp); + return finishedProp; } if (isAsync || isGenerator || this.match(tt.parenL)) { @@ -2174,12 +2304,13 @@ export default class ExpressionParser extends LValParser { // if `isPattern` is true, parse https://tc39.es/ecma262/#prod-BindingProperty // else https://tc39.es/ecma262/#prod-PropertyDefinition parseObjectProperty( - prop: N.ObjectProperty, - startPos: ?number, - startLoc: ?Position, + this: Parser, + prop: Undone, + startPos: number | undefined | null, + startLoc: Position | undefined | null, isPattern: boolean, - refExpressionErrors: ?ExpressionErrors, - ): ?N.ObjectProperty { + refExpressionErrors?: ExpressionErrors | null, + ): N.ObjectProperty | undefined | null { prop.shorthand = false; if (this.eat(tt.colon)) { @@ -2229,25 +2360,26 @@ export default class ExpressionParser extends LValParser { } parseObjPropValue( - prop: any, - startPos: ?number, - startLoc: ?Position, + this: Parser, + prop: Undone, + startPos: number | undefined | null, + startLoc: Position | undefined | null, isGenerator: boolean, isAsync: boolean, isPattern: boolean, isAccessor: boolean, - refExpressionErrors?: ?ExpressionErrors, - ): void { + refExpressionErrors?: ExpressionErrors | null, + ): N.ObjectMethod | N.ObjectProperty { const node = this.parseObjectMethod( - prop, + prop as Undone, isGenerator, isAsync, isPattern, isAccessor, ) || this.parseObjectProperty( - prop, + prop as Undone, startPos, startLoc, isPattern, @@ -2256,7 +2388,6 @@ export default class ExpressionParser extends LValParser { if (!node) this.unexpected(); - // $FlowFixMe return node; } @@ -2264,11 +2395,14 @@ export default class ExpressionParser extends LValParser { // when refExpressionErrors presents, it will parse private name // and record the position of the first private name parsePropertyName( - prop: N.ObjectOrClassMember | N.ClassMember | N.TsNamedTypeElementBase, - refExpressionErrors?: ?ExpressionErrors, + this: Parser, + prop: Undone< + N.ObjectOrClassMember | N.ClassMember | N.TsNamedTypeElementBase + >, + refExpressionErrors?: ExpressionErrors | null, ): N.Expression | N.Identifier { if (this.eat(tt.bracketL)) { - (prop: $FlowSubtype).computed = true; + (prop as Undone).computed = true; prop.key = this.parseMaybeAssignAllowIn(); this.expect(tt.bracketR); } else { @@ -2311,7 +2445,7 @@ export default class ExpressionParser extends LValParser { throw this.unexpected(); } } - (prop: $FlowFixMe).key = key; + (prop as any).key = key; if (type !== tt.privateName) { // ClassPrivateProperty is never computed, so we don't assign in that case. prop.computed = false; @@ -2323,7 +2457,10 @@ export default class ExpressionParser extends LValParser { // Initialize empty function node. - initFunction(node: N.BodilessFunctionOrMethodBase, isAsync: ?boolean): void { + initFunction( + node: N.BodilessFunctionOrMethodBase, + isAsync?: boolean | null, + ): void { node.id = null; node.generator = false; node.async = !!isAsync; @@ -2331,13 +2468,14 @@ export default class ExpressionParser extends LValParser { // Parse object or class method. - parseMethod( - node: T, + parseMethod( + this: Parser, + node: Undone, isGenerator: boolean, isAsync: boolean, isConstructor: boolean, allowDirectSuper: boolean, - type: string, + type: T["type"], inClassScope: boolean = false, ): T { this.initFunction(node, isAsync); @@ -2350,29 +2488,30 @@ export default class ExpressionParser extends LValParser { (allowDirectSuper ? SCOPE_DIRECT_SUPER : 0), ); this.prodParam.enter(functionFlags(isAsync, node.generator)); - this.parseFunctionParams((node: any), allowModifiers); - this.parseFunctionBodyAndFinish(node, type, true); + this.parseFunctionParams(node, allowModifiers); + const finishedNode = this.parseFunctionBodyAndFinish(node, type, true); this.prodParam.exit(); this.scope.exit(); - return node; + return finishedNode; } // parse an array literal or tuple literal // https://tc39.es/ecma262/#prod-ArrayLiteral // https://tc39.es/proposal-record-tuple/#prod-TupleLiteral parseArrayLike( + this: Parser, close: TokenType, canBePattern: boolean, isTuple: boolean, - refExpressionErrors: ?ExpressionErrors, + refExpressionErrors?: ExpressionErrors | null, ): N.ArrayExpression | N.TupleExpression { if (isTuple) { this.expectPlugin("recordAndTuple"); } const oldInFSharpPipelineDirectBody = this.state.inFSharpPipelineDirectBody; this.state.inFSharpPipelineDirectBody = false; - const node = this.startNode(); + const node = this.startNode(); this.next(); node.elements = this.parseExprList( close, @@ -2391,10 +2530,11 @@ export default class ExpressionParser extends LValParser { // If the parameters are provided, they will be converted to an // assignable list. parseArrowExpression( - node: N.ArrowFunctionExpression, - params: ?(N.Expression[]), + this: Parser, + node: Undone, + params: N.Expression[] | undefined | null, isAsync: boolean, - trailingCommaLoc: ?Position, + trailingCommaLoc?: Position | null, ): N.ArrowFunctionExpression { this.scope.enter(SCOPE_FUNCTION | SCOPE_ARROW); let flags = functionFlags(isAsync, false); @@ -2423,36 +2563,41 @@ export default class ExpressionParser extends LValParser { } setArrowFunctionParameters( - node: N.ArrowFunctionExpression, + node: Undone, params: N.Expression[], - trailingCommaLoc: ?Position, + trailingCommaLoc?: Position | null, ): void { this.toAssignableList(params, trailingCommaLoc, false); - node.params = params; + node.params = params as (N.Pattern | N.TSParameterProperty)[]; } - parseFunctionBodyAndFinish( - node: N.BodilessFunctionOrMethodBase, - type: string, - isMethod?: boolean = false, - ): void { - // $FlowIgnore (node is not bodiless if we get here) + parseFunctionBodyAndFinish< + T extends + | N.Function + | N.TSDeclareMethod + | N.TSDeclareFunction + | N.ClassPrivateMethod, + >(node: Undone, type: T["type"], isMethod: boolean = false): T { + // @ts-expect-error (node is not bodiless if we get here) this.parseFunctionBody(node, false, isMethod); - this.finishNode(node, type); + return this.finishNode(node, type); } // Parse function body and check parameters. parseFunctionBody( - node: N.Function, - allowExpression: ?boolean, - isMethod?: boolean = false, + this: Parser, + node: Undone, + allowExpression?: boolean | null, + isMethod: boolean = false, ): void { const isExpression = allowExpression && !this.match(tt.braceL); this.expressionScope.enter(newExpressionScope()); if (isExpression) { // https://tc39.es/ecma262/#prod-ExpressionBody - node.body = this.parseMaybeAssign(); + // @ts-expect-error Fixme: refine typings + (node as Undone).body = + this.parseMaybeAssign(); this.checkParams(node, false, allowExpression, false); } else { const oldStrict = this.state.strict; @@ -2475,11 +2620,12 @@ export default class ExpressionParser extends LValParser { // This logic is here to align the error location with the ESTree plugin. this.raise(Errors.IllegalLanguageModeDirective, { at: - // $FlowIgnore + // @ts-expect-error (node.kind === "method" || node.kind === "constructor") && - // $FlowIgnore + // @ts-expect-error !!node.key - ? node.key.loc.end + ? // @ts-expect-error node.key has been gaurded + node.key.loc.end : node, }); } @@ -2512,7 +2658,7 @@ export default class ExpressionParser extends LValParser { } isSimpleParamList( - params: $ReadOnlyArray, + params: ReadonlyArray, ): boolean { for (let i = 0, len = params.length; i < len; i++) { if (!this.isSimpleParameter(params[i])) return false; @@ -2521,20 +2667,20 @@ export default class ExpressionParser extends LValParser { } checkParams( - node: N.Function, + node: Undone, allowDuplicates: boolean, - // eslint-disable-next-line no-unused-vars - isArrowFunction: ?boolean, - strictModeChanged?: boolean = true, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + isArrowFunction?: boolean | null, + strictModeChanged: boolean = true, ): void { - const checkClashes = !allowDuplicates && new Set(); + const checkClashes = !allowDuplicates && new Set(); // We create a fake node with the "ephemeral" type `FormalParameters`[1] // since we just store an array of parameters. Perhaps someday we can have // something like class FormalParameters extends Array { ... }, which would // also be helpful when traversing this node. // // 1. https://tc39.es/ecma262/#prod-FormalParameters - const formalParameters = { type: "FormalParameters" }; + const formalParameters = { type: "FormalParameters" } as const; for (const param of node.params) { this.checkLVal(param, { in: formalParameters, @@ -2552,12 +2698,13 @@ export default class ExpressionParser extends LValParser { // for array literals). parseExprList( + this: Parser, close: TokenType, allowEmpty?: boolean, - refExpressionErrors?: ?ExpressionErrors, - nodeForExtra?: ?N.Node, - ): $ReadOnlyArray { - const elts = []; + refExpressionErrors?: ExpressionErrors | null, + nodeForExtra?: N.Node | null, + ): (N.Expression | null)[] { + const elts: (N.Expression | null)[] = []; let first = true; while (!this.eat(close)) { @@ -2580,10 +2727,23 @@ export default class ExpressionParser extends LValParser { } parseExprListItem( - allowEmpty: ?boolean, - refExpressionErrors?: ?ExpressionErrors, - allowPlaceholder: ?boolean, - ): ?N.Expression { + this: Parser, + allowEmpty?: boolean, + refExpressionErrors?: ExpressionErrors | null, + allowPlaceholder?: boolean | null, + ): N.Expression | null; + parseExprListItem( + this: Parser, + allowEmpty?: false, + refExpressionErrors?: ExpressionErrors | null, + allowPlaceholder?: boolean | null, + ): N.Expression; + parseExprListItem( + this: Parser, + allowEmpty?: boolean | null, + refExpressionErrors?: ExpressionErrors | null, + allowPlaceholder?: boolean | null, + ): N.Expression | null { let elt; if (this.match(tt.comma)) { if (!allowEmpty) { @@ -2628,13 +2788,16 @@ export default class ExpressionParser extends LValParser { // are not identifiers and cannot contain escape sequences. parseIdentifier(liberal?: boolean): N.Identifier { - const node = this.startNode(); + const node = this.startNode(); const name = this.parseIdentifierName(node.start, liberal); return this.createIdentifier(node, name); } - createIdentifier(node: N.Identifier, name: string): N.Identifier { + createIdentifier( + node: Omit, + name: string, + ): N.Identifier { node.name = name; node.loc.identifierName = name; @@ -2743,8 +2906,12 @@ export default class ExpressionParser extends LValParser { // Parses await expression inside async function. - parseAwait(startPos: number, startLoc: Position): N.AwaitExpression { - const node = this.startNodeAt(startPos, startLoc); + parseAwait( + this: Parser, + startPos: number, + startLoc: Position, + ): N.AwaitExpression { + const node = this.startNodeAt(startPos, startLoc); this.expressionScope.recordParameterInitializerError( Errors.AwaitExpressionFormalParameter, @@ -2792,8 +2959,8 @@ export default class ExpressionParser extends LValParser { // Parses yield expression inside generator. - parseYield(): N.YieldExpression { - const node = this.startNode(); + parseYield(this: Parser): N.YieldExpression { + const node = this.startNode(); this.expressionScope.recordParameterInitializerError( Errors.YieldInParameter, @@ -2802,7 +2969,7 @@ export default class ExpressionParser extends LValParser { this.next(); let delegating = false; - let argument = null; + let argument: N.Expression | null = null; if (!this.hasPrecedingLineBreak()) { delegating = this.eat(tt.star); switch (this.state.type) { @@ -2847,12 +3014,19 @@ export default class ExpressionParser extends LValParser { childExpr: N.Expression, startPos: number, startLoc: Position, - ): N.PipelineBody { - const bodyNode = this.startNodeAt(startPos, startLoc); + ) { if (this.isSimpleReference(childExpr)) { + const bodyNode = this.startNodeAt( + startPos, + startLoc, + ); bodyNode.callee = childExpr; return this.finishNode(bodyNode, "PipelineBareFunction"); } else { + const bodyNode = this.startNodeAt( + startPos, + startLoc, + ); this.checkSmartPipeTopicBodyEarlyErrors(startLoc); bodyNode.expression = childExpr; return this.finishNode(bodyNode, "PipelineTopicExpression"); @@ -3004,7 +3178,7 @@ export default class ExpressionParser extends LValParser { ); } - parseFSharpPipelineBody(prec: number): N.Expression { + parseFSharpPipelineBody(this: Parser, prec: number): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; @@ -3025,7 +3199,7 @@ export default class ExpressionParser extends LValParser { } // https://github.com/tc39/proposal-js-module-blocks - parseModuleExpression(): N.ModuleExpression { + parseModuleExpression(this: Parser): N.ModuleExpression { this.expectPlugin("moduleBlocks"); const node = this.startNode(); this.next(); // eat "module" @@ -3046,7 +3220,7 @@ export default class ExpressionParser extends LValParser { // Used in Flow plugin parsePropertyNamePrefixOperator( - // eslint-disable-next-line no-unused-vars - prop: N.ObjectOrClassMember | N.ClassMember, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + prop: Undone, ): void {} } diff --git a/packages/babel-parser/src/parser/index.js b/packages/babel-parser/src/parser/index.ts similarity index 76% rename from packages/babel-parser/src/parser/index.js rename to packages/babel-parser/src/parser/index.ts index 2db11faf491c..776449a899f5 100644 --- a/packages/babel-parser/src/parser/index.js +++ b/packages/babel-parser/src/parser/index.ts @@ -1,13 +1,16 @@ -// @flow - import type { Options } from "../options"; -import type { File /*::, JSXOpeningElement */ } from "../types"; +import type { File, Program /*::, JSXOpeningElement */ } from "../types"; import type { PluginList } from "../plugin-utils"; import { getOptions } from "../options"; import StatementParser from "./statement"; import ScopeHandler from "../util/scope"; -export type PluginsMap = Map; +export type PluginsMap = Map< + string, + { + [x: string]: any; + } +>; export default class Parser extends StatementParser { // Forward-declaration so typescript plugin can override jsx plugin @@ -17,7 +20,7 @@ export default class Parser extends StatementParser { ) => JSXOpeningElement; */ - constructor(options: ?Options, input: string) { + constructor(options: Options | undefined | null, input: string) { options = getOptions(options); super(options, input); @@ -28,14 +31,16 @@ export default class Parser extends StatementParser { } // This can be overwritten, for example, by the TypeScript plugin. - getScopeHandler(): Class> { + getScopeHandler(): { + new (...args: any): ScopeHandler; + } { return ScopeHandler; } parse(): File { this.enterInitialScopes(); - const file = this.startNode(); - const program = this.startNode(); + const file = this.startNode() as File; + const program = this.startNode() as Program; this.nextToken(); file.errors = null; this.parseTopLevel(file, program); diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.ts similarity index 87% rename from packages/babel-parser/src/parser/lval.js rename to packages/babel-parser/src/parser/lval.ts index 20f883246882..0432891c0692 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.ts @@ -1,9 +1,8 @@ -// @flow - /*:: declare var invariant; */ import * as charCodes from "charcodes"; import { tt, type TokenType } from "../tokenizer/types"; import type { + AssignmentPattern, TSParameterProperty, Decorator, Expression, @@ -15,17 +14,20 @@ import type { /*:: ObjectOrClassMember, */ /*:: ClassMember, */ ObjectMember, + ObjectExpression, + ArrayExpression, + ArrayPattern, /*:: TsNamedTypeElementBase, */ /*:: PrivateName, */ /*:: ObjectExpression, */ /*:: ObjectPattern, */ } from "../types"; -import type { Pos, Position } from "../util/location"; +import type { Position } from "../util/location"; import { isStrictBindOnlyReservedWord, isStrictBindReservedWord, } from "../util/identifier"; -import { NodeUtils } from "./node"; +import { NodeUtils, type Undone } from "./node"; import { type BindingTypes, BIND_NONE, @@ -33,8 +35,9 @@ import { } from "../util/scopeflags"; import { ExpressionErrors } from "./util"; import { Errors, type LValAncestor } from "../parse-error"; +import type Parser from "./index"; -const getOwn = (object, key) => +const getOwn = (object: T, key: keyof T) => Object.hasOwnProperty.call(object, key) && object[key]; const unwrapParenthesizedExpression = (node: Node): Node => { @@ -50,7 +53,6 @@ export default class LValParser extends NodeUtils { +parseMaybeAssignAllowIn: ( refExpressionErrors?: ?ExpressionErrors, afterLeftParse?: Function, - refNeedsArrowPos?: ?Pos, ) => Expression; +parseObjectLike: ( close: TokenType, @@ -230,7 +232,7 @@ export default class LValParser extends NodeUtils { toAssignableList( exprList: Expression[], - trailingCommaLoc?: ?Position, + trailingCommaLoc: Position | undefined | null, isLHS: boolean, ): void { const end = exprList.length - 1; @@ -269,13 +271,15 @@ export default class LValParser extends NodeUtils { case "ObjectExpression": { const last = node.properties.length - 1; - return node.properties.every((prop, i) => { - return ( - prop.type !== "ObjectMethod" && - (i === last || prop.type !== "SpreadElement") && - this.isAssignable(prop) - ); - }); + return (node.properties as ObjectExpression["properties"]).every( + (prop, i) => { + return ( + prop.type !== "ObjectMethod" && + (i === last || prop.type !== "SpreadElement") && + this.isAssignable(prop) + ); + }, + ); } case "ObjectProperty": @@ -285,7 +289,7 @@ export default class LValParser extends NodeUtils { return this.isAssignable(node.argument); case "ArrayExpression": - return node.elements.every( + return (node as ArrayExpression).elements.every( element => element === null || this.isAssignable(element), ); @@ -307,14 +311,15 @@ export default class LValParser extends NodeUtils { // Convert list of expression atoms to a list of toReferencedList( - exprList: $ReadOnlyArray, - isParenthesizedExpr?: boolean, // eslint-disable-line no-unused-vars - ): $ReadOnlyArray { + exprList: ReadonlyArray, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + isParenthesizedExpr?: boolean, + ): ReadonlyArray { return exprList; } toReferencedListDeep( - exprList: $ReadOnlyArray, + exprList: ReadonlyArray, isParenthesizedExpr?: boolean, ): void { this.toReferencedList(exprList, isParenthesizedExpr); @@ -329,35 +334,35 @@ export default class LValParser extends NodeUtils { // Parses spread element. parseSpread( - refExpressionErrors: ?ExpressionErrors, - refNeedsArrowPos?: ?Pos, + this: Parser, + refExpressionErrors?: ExpressionErrors | null, ): SpreadElement { - const node = this.startNode(); + const node = this.startNode(); this.next(); node.argument = this.parseMaybeAssignAllowIn( refExpressionErrors, undefined, - refNeedsArrowPos, ); return this.finishNode(node, "SpreadElement"); } // https://tc39.es/ecma262/#prod-BindingRestProperty // https://tc39.es/ecma262/#prod-BindingRestElement - parseRestBinding(): RestElement { - const node = this.startNode(); + parseRestBinding(this: Parser): RestElement { + const node = this.startNode(); this.next(); // eat `...` node.argument = this.parseBindingAtom(); return this.finishNode(node, "RestElement"); } // Parses lvalue (assignable) atom. - parseBindingAtom(): Pattern { + parseBindingAtom(this: Parser): Pattern { // https://tc39.es/ecma262/#prod-BindingPattern switch (this.state.type) { case tt.bracketL: { - const node = this.startNode(); + const node = this.startNode(); this.next(); + // @ts-expect-error: Fixme: TSParameterProperty can not be assigned to node.elements node.elements = this.parseBindingList( tt.bracketR, charCodes.rightSquareBracket, @@ -376,11 +381,12 @@ export default class LValParser extends NodeUtils { // https://tc39.es/ecma262/#prod-BindingElementList parseBindingList( + this: Parser, close: TokenType, - closeCharCode: $Values, + closeCharCode: typeof charCodes[keyof typeof charCodes], allowEmpty?: boolean, allowModifiers?: boolean, - ): $ReadOnlyArray { + ): Array { const elts: Array = []; let first = true; while (!this.eat(close)) { @@ -390,7 +396,6 @@ export default class LValParser extends NodeUtils { this.expect(tt.comma); } if (allowEmpty && this.match(tt.comma)) { - // $FlowFixMe This method returns `$ReadOnlyArray` if `allowEmpty` is set. elts.push(null); } else if (this.eat(close)) { break; @@ -418,7 +423,10 @@ export default class LValParser extends NodeUtils { } // https://tc39.es/ecma262/#prod-BindingRestProperty - parseBindingRestProperty(prop: RestElement): RestElement { + parseBindingRestProperty( + this: Parser, + prop: Undone, + ): RestElement { this.next(); // eat '...' // Don't use parseRestBinding() as we only allow Identifier here. prop.argument = this.parseIdentifier(); @@ -427,21 +435,21 @@ export default class LValParser extends NodeUtils { } // https://tc39.es/ecma262/#prod-BindingProperty - parseBindingProperty(): ObjectMember | RestElement { - const prop = this.startNode(); + parseBindingProperty(this: Parser): ObjectMember | RestElement { + const prop = this.startNode(); const { type, start: startPos, startLoc } = this.state; if (type === tt.ellipsis) { - return this.parseBindingRestProperty(prop); + return this.parseBindingRestProperty(prop as Undone); } else if (type === tt.privateName) { this.expectPlugin("destructuringPrivate", startLoc); this.classScope.usePrivateName(this.state.value, startLoc); - prop.key = this.parsePrivateName(); + (prop as Undone).key = this.parsePrivateName(); } else { - this.parsePropertyName(prop); + this.parsePropertyName(prop as Undone); } - prop.method = false; - this.parseObjPropValue( - prop, + (prop as Undone).method = false; + return this.parseObjPropValue( + prop as Undone, startPos, startLoc, false /* isGenerator */, @@ -449,12 +457,11 @@ export default class LValParser extends NodeUtils { true /* isPattern */, false /* isAccessor */, ); - - return prop; } parseAssignableListItem( - allowModifiers: ?boolean, + this: Parser, + allowModifiers: boolean | undefined | null, decorators: Decorator[], ): Pattern | TSParameterProperty { const left = this.parseMaybeDefault(); @@ -474,17 +481,17 @@ export default class LValParser extends NodeUtils { // Parses assignment pattern around given atom if possible. // https://tc39.es/ecma262/#prod-BindingElement parseMaybeDefault( - startPos?: ?number, - startLoc?: ?Position, - left?: ?Pattern, + this: Parser, + startPos?: number | null, + startLoc?: Position | null, + left?: Pattern | null, ): Pattern { startLoc = startLoc ?? this.state.startLoc; startPos = startPos ?? this.state.start; - // $FlowIgnore left = left ?? this.parseBindingAtom(); if (!this.eat(tt.eq)) return left; - const node = this.startNodeAt(startPos, startLoc); + const node = this.startNodeAt(startPos, startLoc); node.left = left; node.right = this.parseMaybeAssignAllowIn(); return this.finishNode(node, "AssignmentPattern"); @@ -521,11 +528,11 @@ export default class LValParser extends NodeUtils { */ isValidLVal( type: string, - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars isUnparenthesizedInAssign: boolean, - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars binding: BindingTypes, - ) { + ): string | boolean { return getOwn( { AssignmentPattern: "left", @@ -535,6 +542,7 @@ export default class LValParser extends NodeUtils { ArrayPattern: "elements", ObjectPattern: "properties", }, + // @ts-expect-error refine string to enum type, ); } @@ -579,12 +587,12 @@ export default class LValParser extends NodeUtils { allowingSloppyLetBinding = !(binding & BIND_SCOPE_LEXICAL), hasParenthesizedAncestor = false, }: { - in: LValAncestor, - binding?: BindingTypes, - checkClashes?: Set | false, - strictModeChanged?: boolean, - allowingSloppyLetBinding?: boolean, - hasParenthesizedAncestor?: boolean, + in: LValAncestor; + binding?: BindingTypes; + checkClashes?: Set | false; + strictModeChanged?: boolean; + allowingSloppyLetBinding?: boolean; + hasParenthesizedAncestor?: boolean; }, ): void { const type = expression.type; @@ -603,13 +611,13 @@ export default class LValParser extends NodeUtils { if (expression.type === "Identifier") { this.checkIdentifier( - expression, + expression as Identifier, binding, strictModeChanged, allowingSloppyLetBinding, ); - const { name } = expression; + const { name } = expression as Identifier; if (checkClashes) { if (checkClashes.has(name)) { @@ -630,7 +638,6 @@ export default class LValParser extends NodeUtils { ); if (validity === true) return; - if (validity === false) { const ParseErrorClass = binding === BIND_NONE ? Errors.InvalidLhs : Errors.InvalidLhsBinding; @@ -657,10 +664,11 @@ export default class LValParser extends NodeUtils { // Flow has difficulty tracking `key` and `expression`, but only if we use // null-proto objects. If we use normal objects, everything works fine. - // $FlowIgnore + // @ts-expect-error for (const child of [].concat(expression[key])) { if (child) { this.checkLVal(child, { + // @ts-expect-error: refine types in: nextAncestor, binding, checkClashes, @@ -724,7 +732,9 @@ export default class LValParser extends NodeUtils { } } - checkCommaAfterRest(close: $Values): boolean { + checkCommaAfterRest( + close: typeof charCodes[keyof typeof charCodes], + ): boolean { if (!this.match(tt.comma)) { return false; } diff --git a/packages/babel-parser/src/parser/node.js b/packages/babel-parser/src/parser/node.ts similarity index 81% rename from packages/babel-parser/src/parser/node.js rename to packages/babel-parser/src/parser/node.ts index b12c533ce96b..360ed707f6e0 100644 --- a/packages/babel-parser/src/parser/node.js +++ b/packages/babel-parser/src/parser/node.ts @@ -1,5 +1,3 @@ -// @flow - import type Parser from "./index"; import UtilParser from "./util"; import { SourceLocation, type Position } from "../util/location"; @@ -24,14 +22,16 @@ class Node implements NodeBase { declare leadingComments: Array; declare trailingComments: Array; declare innerComments: Array; - declare extra: { [key: string]: any }; + declare extra: { + [key: string]: any; + }; } const NodePrototype = Node.prototype; if (!process.env.BABEL_8_BREAKING) { - // $FlowIgnore + // @ts-expect-error NodePrototype.__clone = function (): Node { - // $FlowIgnore + // @ts-expect-error const newNode: any = new Node(); const keys = Object.keys(this); for (let i = 0, length = keys.length; i < length; i++) { @@ -42,7 +42,9 @@ if (!process.env.BABEL_8_BREAKING) { key !== "trailingComments" && key !== "innerComments" ) { - newNode[key] = this[key]; + newNode[key] = + // @ts-expect-error: key must present in this + this[key]; } } @@ -94,43 +96,51 @@ export function cloneStringLiteral(node: any): any { return cloned; } +export type Undone = Omit; + export class NodeUtils extends UtilParser { - startNode(): T { - // $FlowIgnore + startNode(): Undone { + // @ts-expect-error return new Node(this, this.state.start, this.state.startLoc); } - startNodeAt(pos: number, loc: Position): T { - // $FlowIgnore + startNodeAt(pos: number, loc: Position): Undone { + // @ts-expect-error return new Node(this, pos, loc); } /** Start a new node with a previous node's location. */ - startNodeAtNode(type: NodeType): T { + startNodeAtNode(type: Undone): Undone { return this.startNodeAt(type.start, type.loc.start); } // Finish an AST node, adding `type` and `end` properties. - finishNode(node: T, type: string): T { + finishNode(node: Undone, type: T["type"]): T { return this.finishNodeAt(node, type, this.state.lastTokEndLoc); } // Finish node at given position - finishNodeAt(node: T, type: string, endLoc: Position): T { + finishNodeAt( + node: Omit, + type: T["type"], + endLoc: Position, + ): T { if (process.env.NODE_ENV !== "production" && node.end > 0) { throw new Error( "Do not call finishNode*() twice on the same node." + " Instead use resetEndLocation() or change type directly.", ); } + // @ts-expect-error migrate to Babel types AST typings node.type = type; + // @ts-expect-error migrate to Babel types AST typings node.end = endLoc.index; node.loc.end = endLoc; if (this.options.ranges) node.range[1] = endLoc.index; - if (this.options.attachComment) this.processComment(node); - return node; + if (this.options.attachComment) this.processComment(node as T); + return node as T; } resetStartLocation(node: NodeBase, start: number, startLoc: Position): void { @@ -141,7 +151,7 @@ export class NodeUtils extends UtilParser { resetEndLocation( node: NodeBase, - endLoc?: Position = this.state.lastTokEndLoc, + endLoc: Position = this.state.lastTokEndLoc, ): void { node.end = endLoc.index; node.loc.end = endLoc; diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.ts similarity index 83% rename from packages/babel-parser/src/parser/statement.js rename to packages/babel-parser/src/parser/statement.ts index 5c9aa4b6838f..9ea9847be973 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.ts @@ -1,5 +1,3 @@ -// @flow - import * as N from "../types"; import { tokenIsIdentifier, @@ -41,10 +39,11 @@ import { import type { SourceType } from "../options"; import { Token } from "../tokenizer"; import { Position, createPositionWithColumnOffset } from "../util/location"; -import { cloneStringLiteral, cloneIdentifier } from "./node"; +import { cloneStringLiteral, cloneIdentifier, type Undone } from "./node"; +import type Parser from "./index"; -const loopLabel = { kind: "loop" }, - switchLabel = { kind: "switch" }; +const loopLabel = { kind: "loop" } as const, + switchLabel = { kind: "switch" } as const; const FUNC_NO_FLAGS = 0b000, FUNC_STATEMENT = 0b001, @@ -65,7 +64,7 @@ const keywordRelationalOperator = /in(?:stanceof)?/y; * @param {*} tokens * @returns */ -function babel7CompatTokens(tokens, input) { +function babel7CompatTokens(tokens: (Token | N.Comment)[], input: string) { for (let i = 0; i < tokens.length; i++) { const token = tokens[i]; const { type } = token; @@ -78,8 +77,8 @@ function babel7CompatTokens(tokens, input) { tokens.splice( i, 1, - // $FlowIgnore: hacky way to create token new Token({ + // @ts-expect-error: hacky way to create token type: getExportedToken(tt.hash), value: "#", start: start, @@ -87,8 +86,8 @@ function babel7CompatTokens(tokens, input) { startLoc: loc.start, endLoc: hashEndLoc, }), - // $FlowIgnore: hacky way to create token new Token({ + // @ts-expect-error: hacky way to create token type: getExportedToken(tt.name), value: value, start: hashEndPos, @@ -107,8 +106,8 @@ function babel7CompatTokens(tokens, input) { const backquoteEndLoc = createPositionWithColumnOffset(loc.start, 1); let startToken; if (input.charCodeAt(start) === charCodes.graveAccent) { - // $FlowIgnore: hacky way to create token startToken = new Token({ + // @ts-expect-error: hacky way to create token type: getExportedToken(tt.backQuote), value: "`", start: start, @@ -117,8 +116,8 @@ function babel7CompatTokens(tokens, input) { endLoc: backquoteEndLoc, }); } else { - // $FlowIgnore: hacky way to create token startToken = new Token({ + // @ts-expect-error: hacky way to create token type: getExportedToken(tt.braceR), value: "}", start: start, @@ -136,8 +135,8 @@ function babel7CompatTokens(tokens, input) { templateElementEnd = end - 1; templateElementEndLoc = createPositionWithColumnOffset(loc.end, -1); templateValue = value === null ? null : value.slice(1, -1); - // $FlowIgnore: hacky way to create token endToken = new Token({ + // @ts-expect-error: hacky way to create token type: getExportedToken(tt.backQuote), value: "`", start: templateElementEnd, @@ -150,8 +149,8 @@ function babel7CompatTokens(tokens, input) { templateElementEnd = end - 2; templateElementEndLoc = createPositionWithColumnOffset(loc.end, -2); templateValue = value === null ? null : value.slice(1, -2); - // $FlowIgnore: hacky way to create token endToken = new Token({ + // @ts-expect-error: hacky way to create token type: getExportedToken(tt.dollarBraceL), value: "${", start: templateElementEnd, @@ -164,8 +163,8 @@ function babel7CompatTokens(tokens, input) { i, 1, startToken, - // $FlowIgnore: hacky way to create token new Token({ + // @ts-expect-error: hacky way to create token type: getExportedToken(tt.template), value: templateValue, start: backquoteEnd, @@ -179,7 +178,7 @@ function babel7CompatTokens(tokens, input) { continue; } } - // $FlowIgnore: we manipulate `token` for performance reasons + // @ts-expect-error: we manipulate `token` for performance reasons token.type = getExportedToken(type); } } @@ -193,7 +192,7 @@ export default class StatementParser extends ExpressionParser { // `program` argument. If present, the statements will be appended // to its body instead of creating a new node. - parseTopLevel(file: N.File, program: N.Program): N.File { + parseTopLevel(this: Parser, file: N.File, program: N.Program): N.File { file.program = this.parseProgram(program); file.comments = this.state.comments; @@ -205,7 +204,8 @@ export default class StatementParser extends ExpressionParser { } parseProgram( - program: N.Program, + this: Parser, + program: Undone, end: TokenType = tt.eof, sourceType: SourceType = this.options.sourceType, ): N.Program { @@ -221,7 +221,7 @@ export default class StatementParser extends ExpressionParser { this.raise(Errors.ModuleExportUndefined, { at, localName }); } } - return this.finishNode(program, "Program"); + return this.finishNode(program, "Program"); } // TODO @@ -234,7 +234,7 @@ export default class StatementParser extends ExpressionParser { * @memberof StatementParser */ stmtToDirective(stmt: N.Statement): N.Directive { - const directive = (stmt: any); + const directive = stmt as any; directive.type = "Directive"; directive.value = directive.expression; delete directive.expression; @@ -258,13 +258,13 @@ export default class StatementParser extends ExpressionParser { return null; } - const node = this.startNode(); + const node = this.startNode(); node.value = this.state.value; this.next(); return this.finishNode(node, "InterpreterDirective"); } - isLet(context: ?string): boolean { + isLet(context?: string | null): boolean { if (!this.isContextual(tt._let)) { return false; } @@ -280,7 +280,7 @@ export default class StatementParser extends ExpressionParser { * @returns {boolean} * @memberof StatementParser */ - isLetKeyword(context: ?string): boolean { + isLetKeyword(context?: string | null): boolean { const next = this.nextTokenStart(); const nextCh = this.codePointAtPos(next); // For ambiguous cases, determine if a LexicalDeclaration (or only a @@ -322,14 +322,22 @@ export default class StatementParser extends ExpressionParser { // https://tc39.es/ecma262/#prod-Statement // ImportDeclaration and ExportDeclaration are also handled here so we can throw recoverable errors // when they are not at the top level - parseStatement(context: ?string, topLevel?: boolean): N.Statement { + parseStatement( + this: Parser, + context?: string | null, + topLevel?: boolean, + ): N.Statement { if (this.match(tt.at)) { this.parseDecorators(true); } return this.parseStatementContent(context, topLevel); } - parseStatementContent(context: ?string, topLevel: ?boolean): N.Statement { + parseStatementContent( + this: Parser, + context?: string | null, + topLevel?: boolean | null, + ): N.Statement { let starttype = this.state.type; const node = this.startNode(); let kind; @@ -349,11 +357,11 @@ export default class StatementParser extends ExpressionParser { case tt._continue: return this.parseBreakContinueStatement(node, /* isBreak */ false); case tt._debugger: - return this.parseDebuggerStatement(node); + return this.parseDebuggerStatement(node as Undone); case tt._do: - return this.parseDoStatement(node); + return this.parseDoStatement(node as Undone); case tt._for: - return this.parseForStatement(node); + return this.parseForStatement(node as Undone); case tt._function: if (this.lookaheadCharCode() === charCodes.dot) break; if (context) { @@ -363,22 +371,26 @@ export default class StatementParser extends ExpressionParser { this.raise(Errors.SloppyFunction, { at: this.state.startLoc }); } } - return this.parseFunctionStatement(node, false, !context); + return this.parseFunctionStatement( + node as Undone, + false, + !context, + ); case tt._class: if (context) this.unexpected(); - return this.parseClass(node, true); + return this.parseClass(node as Undone, true); case tt._if: - return this.parseIfStatement(node); + return this.parseIfStatement(node as Undone); case tt._return: - return this.parseReturnStatement(node); + return this.parseReturnStatement(node as Undone); case tt._switch: - return this.parseSwitchStatement(node); + return this.parseSwitchStatement(node as Undone); case tt._throw: - return this.parseThrowStatement(node); + return this.parseThrowStatement(node as Undone); case tt._try: - return this.parseTryStatement(node); + return this.parseTryStatement(node as Undone); case tt._const: case tt._var: @@ -388,16 +400,19 @@ export default class StatementParser extends ExpressionParser { at: this.state.startLoc, }); } - return this.parseVarStatement(node, kind); + return this.parseVarStatement( + node as Undone, + kind, + ); case tt._while: - return this.parseWhileStatement(node); + return this.parseWhileStatement(node as Undone); case tt._with: - return this.parseWithStatement(node); + return this.parseWithStatement(node as Undone); case tt.braceL: return this.parseBlock(); case tt.semi: - return this.parseEmptyStatement(node); + return this.parseEmptyStatement(node as Undone); case tt._import: { const nextTokenCharCode = this.lookaheadCharCode(); if ( @@ -419,7 +434,7 @@ export default class StatementParser extends ExpressionParser { let result; if (starttype === tt._import) { - result = this.parseImport(node); + result = this.parseImport(node as Undone); if ( result.type === "ImportDeclaration" && @@ -428,7 +443,13 @@ export default class StatementParser extends ExpressionParser { this.sawUnambiguousESM = true; } } else { - result = this.parseExport(node); + result = this.parseExport( + node as Undone< + | N.ExportAllDeclaration + | N.ExportDefaultDeclaration + | N.ExportDefaultDeclaration + >, + ); if ( (result.type === "ExportNamedDeclaration" && @@ -441,7 +462,7 @@ export default class StatementParser extends ExpressionParser { } } - this.assertModuleNodeAllowed(node); + this.assertModuleNodeAllowed(result); return result; } @@ -454,7 +475,11 @@ export default class StatementParser extends ExpressionParser { }); } this.next(); - return this.parseFunctionStatement(node, true, !context); + return this.parseFunctionStatement( + node as Undone, + true, + !context, + ); } } } @@ -472,9 +497,18 @@ export default class StatementParser extends ExpressionParser { expr.type === "Identifier" && this.eat(tt.colon) ) { - return this.parseLabeledStatement(node, maybeName, expr, context); + return this.parseLabeledStatement( + node as Undone, + maybeName, + // @ts-expect-error migrate to Babel types + expr, + context, + ); } else { - return this.parseExpressionStatement(node, expr); + return this.parseExpressionStatement( + node as Undone, + expr, + ); } } @@ -498,7 +532,7 @@ export default class StatementParser extends ExpressionParser { return this.match(tt._class); } - parseDecorators(allowExport?: boolean): void { + parseDecorators(this: Parser, allowExport?: boolean): void { const currentContextDecorators = this.state.decoratorStack[this.state.decoratorStack.length - 1]; while (this.match(tt.at)) { @@ -524,10 +558,10 @@ export default class StatementParser extends ExpressionParser { } } - parseDecorator(): N.Decorator { + parseDecorator(this: Parser): N.Decorator { this.expectOnePlugin(["decorators", "decorators-legacy"]); - const node = this.startNode(); + const node = this.startNode(); this.next(); if (this.hasPlugin("decorators")) { @@ -566,7 +600,7 @@ export default class StatementParser extends ExpressionParser { return this.finishNode(node, "Decorator"); } - parseMaybeDecoratorArguments(expr: N.Expression): N.Expression { + parseMaybeDecoratorArguments(this: Parser, expr: N.Expression): N.Expression { if (this.eat(tt.parenL)) { const node = this.startNodeAtNode(expr); node.callee = expr; @@ -579,7 +613,15 @@ export default class StatementParser extends ExpressionParser { } parseBreakContinueStatement( - node: N.BreakStatement | N.ContinueStatement, + node: Undone, + isBreak: true, + ): N.BreakStatement; + parseBreakContinueStatement( + node: Undone, + isBreak: false, + ): N.ContinueStatement; + parseBreakContinueStatement( + node: Undone, isBreak: boolean, ): N.BreakStatement | N.ContinueStatement { this.next(); @@ -600,7 +642,7 @@ export default class StatementParser extends ExpressionParser { } verifyBreakContinue( - node: N.BreakStatement | N.ContinueStatement, + node: Undone, isBreak: boolean, ) { let i; @@ -617,20 +659,25 @@ export default class StatementParser extends ExpressionParser { } } - parseDebuggerStatement(node: N.DebuggerStatement): N.DebuggerStatement { + parseDebuggerStatement( + node: Undone, + ): N.DebuggerStatement { this.next(); this.semicolon(); return this.finishNode(node, "DebuggerStatement"); } - parseHeaderExpression(): N.Expression { + parseHeaderExpression(this: Parser): N.Expression { this.expect(tt.parenL); const val = this.parseExpression(); this.expect(tt.parenR); return val; } - parseDoStatement(node: N.DoWhileStatement): N.DoWhileStatement { + parseDoStatement( + this: Parser, + node: Undone, + ): N.DoWhileStatement { this.next(); this.state.labels.push(loopLabel); @@ -660,7 +707,10 @@ export default class StatementParser extends ExpressionParser { // part (semicolon immediately after the opening parenthesis), it // is a regular `for` loop. - parseForStatement(node: N.Node): N.ForLike { + parseForStatement( + this: Parser, + node: Undone, + ): N.ForLike { this.next(); this.state.labels.push(loopLabel); @@ -676,28 +726,28 @@ export default class StatementParser extends ExpressionParser { if (awaitAt !== null) { this.unexpected(awaitAt); } - return this.parseFor(node, null); + return this.parseFor(node as Undone, null); } const startsWithLet = this.isContextual(tt._let); const isLet = startsWithLet && this.isLetKeyword(); if (this.match(tt._var) || this.match(tt._const) || isLet) { - const init = this.startNode(); + const initNode = this.startNode(); const kind = isLet ? "let" : this.state.value; this.next(); - this.parseVar(init, true, kind); - this.finishNode(init, "VariableDeclaration"); + this.parseVar(initNode, true, kind); + const init = this.finishNode(initNode, "VariableDeclaration"); if ( (this.match(tt._in) || this.isContextual(tt._of)) && init.declarations.length === 1 ) { - return this.parseForIn(node, init, awaitAt); + return this.parseForIn(node as Undone, init, awaitAt); } if (awaitAt !== null) { this.unexpected(awaitAt); } - return this.parseFor(node, init); + return this.parseFor(node as Undone, init); } // Check whether the first token is possibly a contextual keyword, so that @@ -731,18 +781,24 @@ export default class StatementParser extends ExpressionParser { this.toAssignable(init, /* isLHS */ true); const type = isForOf ? "ForOfStatement" : "ForInStatement"; this.checkLVal(init, { in: { type } }); - return this.parseForIn(node, init, awaitAt); + return this.parseForIn( + node as Undone, + // @ts-expect-error init has been transformed to an assignable + init, + awaitAt, + ); } else { this.checkExpressionErrors(refExpressionErrors, true); } if (awaitAt !== null) { this.unexpected(awaitAt); } - return this.parseFor(node, init); + return this.parseFor(node as Undone, init); } parseFunctionStatement( - node: N.FunctionDeclaration, + this: Parser, + node: Undone, isAsync?: boolean, declarationPosition?: boolean, ): N.FunctionDeclaration { @@ -754,7 +810,7 @@ export default class StatementParser extends ExpressionParser { ); } - parseIfStatement(node: N.IfStatement): N.IfStatement { + parseIfStatement(this: Parser, node: Undone) { this.next(); node.test = this.parseHeaderExpression(); node.consequent = this.parseStatement("if"); @@ -762,7 +818,7 @@ export default class StatementParser extends ExpressionParser { return this.finishNode(node, "IfStatement"); } - parseReturnStatement(node: N.ReturnStatement): N.ReturnStatement { + parseReturnStatement(this: Parser, node: Undone) { if (!this.prodParam.hasReturn && !this.options.allowReturnOutsideFunction) { this.raise(Errors.IllegalReturn, { at: this.state.startLoc }); } @@ -783,10 +839,10 @@ export default class StatementParser extends ExpressionParser { return this.finishNode(node, "ReturnStatement"); } - parseSwitchStatement(node: N.SwitchStatement): N.SwitchStatement { + parseSwitchStatement(this: Parser, node: Undone) { this.next(); node.discriminant = this.parseHeaderExpression(); - const cases = (node.cases = []); + const cases: N.SwitchStatement["cases"] = (node.cases = []); this.expect(tt.braceL); this.state.labels.push(switchLabel); this.scope.enter(SCOPE_OTHER); @@ -800,6 +856,7 @@ export default class StatementParser extends ExpressionParser { if (this.match(tt._case) || this.match(tt._default)) { const isCase = this.match(tt._case); if (cur) this.finishNode(cur, "SwitchCase"); + // @ts-expect-error Fixme cases.push((cur = this.startNode())); cur.consequent = []; this.next(); @@ -830,7 +887,7 @@ export default class StatementParser extends ExpressionParser { return this.finishNode(node, "SwitchStatement"); } - parseThrowStatement(node: N.ThrowStatement): N.ThrowStatement { + parseThrowStatement(this: Parser, node: Undone) { this.next(); if (this.hasPrecedingLineBreak()) { this.raise(Errors.NewlineAfterThrow, { at: this.state.lastTokEndLoc }); @@ -840,7 +897,7 @@ export default class StatementParser extends ExpressionParser { return this.finishNode(node, "ThrowStatement"); } - parseCatchClauseParam(): N.Pattern { + parseCatchClauseParam(this: Parser): N.Pattern { const param = this.parseBindingAtom(); const simple = param.type === "Identifier"; @@ -854,14 +911,17 @@ export default class StatementParser extends ExpressionParser { return param; } - parseTryStatement(node: N.TryStatement): N.TryStatement { + parseTryStatement( + this: Parser, + node: Undone, + ): N.TryStatement { this.next(); node.block = this.parseBlock(); node.handler = null; if (this.match(tt._catch)) { - const clause = this.startNode(); + const clause = this.startNode(); this.next(); if (this.match(tt.parenL)) { this.expect(tt.parenL); @@ -895,7 +955,8 @@ export default class StatementParser extends ExpressionParser { } parseVarStatement( - node: N.VariableDeclaration, + this: Parser, + node: Undone, kind: "var" | "let" | "const", allowMissingInitializer: boolean = false, ): N.VariableDeclaration { @@ -905,7 +966,10 @@ export default class StatementParser extends ExpressionParser { return this.finishNode(node, "VariableDeclaration"); } - parseWhileStatement(node: N.WhileStatement): N.WhileStatement { + parseWhileStatement( + this: Parser, + node: Undone, + ): N.WhileStatement { this.next(); node.test = this.parseHeaderExpression(); this.state.labels.push(loopLabel); @@ -925,7 +989,10 @@ export default class StatementParser extends ExpressionParser { return this.finishNode(node, "WhileStatement"); } - parseWithStatement(node: N.WithStatement): N.WithStatement { + parseWithStatement( + this: Parser, + node: Undone, + ): N.WithStatement { if (this.state.strict) { this.raise(Errors.StrictWith, { at: this.state.startLoc }); } @@ -946,16 +1013,17 @@ export default class StatementParser extends ExpressionParser { return this.finishNode(node, "WithStatement"); } - parseEmptyStatement(node: N.EmptyStatement): N.EmptyStatement { + parseEmptyStatement(node: Undone): N.EmptyStatement { this.next(); return this.finishNode(node, "EmptyStatement"); } parseLabeledStatement( - node: N.LabeledStatement, + this: Parser, + node: Undone, maybeName: string, expr: N.Identifier, - context: ?string, + context?: string | null, ): N.LabeledStatement { for (const label of this.state.labels) { if (label.name === maybeName) { @@ -1000,9 +1068,9 @@ export default class StatementParser extends ExpressionParser { } parseExpressionStatement( - node: N.ExpressionStatement, + node: Undone, expr: N.Expression, - ): N.Statement { + ) { node.expression = expr; this.semicolon(); return this.finishNode(node, "ExpressionStatement"); @@ -1013,11 +1081,12 @@ export default class StatementParser extends ExpressionParser { // function bodies). parseBlock( - allowDirectives?: boolean = false, - createNewLexicalScope?: boolean = true, + this: Parser, + allowDirectives: boolean = false, + createNewLexicalScope: boolean = true, afterBlockParse?: (hasStrictModeDirective: boolean) => void, ): N.BlockStatement { - const node = this.startNode(); + const node = this.startNode(); if (allowDirectives) { this.state.strictErrors.clear(); } @@ -1047,14 +1116,16 @@ export default class StatementParser extends ExpressionParser { } parseBlockBody( - node: N.BlockStatementLike, - allowDirectives: ?boolean, + this: Parser, + node: Undone, + allowDirectives: boolean | undefined | null, topLevel: boolean, end: TokenType, afterBlockParse?: (hasStrictModeDirective: boolean) => void, ): void { - const body = (node.body = []); - const directives = (node.directives = []); + const body: N.BlockStatementLike["body"] = (node.body = []); + const directives: N.BlockStatementLike["directives"] = (node.directives = + []); this.parseBlockOrModuleBlockBody( body, allowDirectives ? directives : undefined, @@ -1068,8 +1139,9 @@ export default class StatementParser extends ExpressionParser { // https://tc39.es/ecma262/#prod-Block // https://tc39.es/ecma262/#prod-ModuleBody parseBlockOrModuleBlockBody( + this: Parser, body: N.Statement[], - directives: ?(N.Directive[]), + directives: N.Directive[] | undefined | null, topLevel: boolean, end: TokenType, afterBlockParse?: (hasStrictModeDirective: boolean) => void, @@ -1088,6 +1160,7 @@ export default class StatementParser extends ExpressionParser { if ( !hasStrictModeDirective && + // @ts-expect-error migrate to Babel types directive.value.value === "use strict" ) { hasStrictModeDirective = true; @@ -1119,8 +1192,9 @@ export default class StatementParser extends ExpressionParser { // expression. parseFor( - node: N.ForStatement, - init: ?(N.VariableDeclaration | N.Expression), + this: Parser, + node: Undone, + init?: N.VariableDeclaration | N.Expression | null, ): N.ForStatement { node.init = init; this.semicolon(/* allowAsi */ false); @@ -1149,9 +1223,10 @@ export default class StatementParser extends ExpressionParser { // same from parser's perspective. parseForIn( - node: N.ForInOf, + this: Parser, + node: Undone, init: N.VariableDeclaration | N.AssignmentPattern, - awaitAt: ?Position, + awaitAt?: Position | null, ): N.ForInOf { const isForIn = this.match(tt._in); this.next(); @@ -1208,15 +1283,16 @@ export default class StatementParser extends ExpressionParser { // Parse a list of variable declarations. parseVar( - node: N.VariableDeclaration, + this: Parser, + node: Undone, isFor: boolean, kind: "var" | "let" | "const", allowMissingInitializer: boolean = false, - ): N.VariableDeclaration { - const declarations = (node.declarations = []); + ): Undone { + const declarations: N.VariableDeclarator[] = (node.declarations = []); node.kind = kind; for (;;) { - const decl = this.startNode(); + const decl = this.startNode(); this.parseVarId(decl, kind); decl.init = !this.eat(tt.eq) ? null @@ -1249,7 +1325,11 @@ export default class StatementParser extends ExpressionParser { return node; } - parseVarId(decl: N.VariableDeclarator, kind: "var" | "let" | "const"): void { + parseVarId( + this: Parser, + decl: Undone, + kind: "var" | "let" | "const", + ): void { decl.id = this.parseBindingAtom(); this.checkLVal(decl.id, { in: { type: "VariableDeclarator" }, @@ -1260,10 +1340,11 @@ export default class StatementParser extends ExpressionParser { // Parse a function declaration or literal (depending on the // `isStatement` parameter). - parseFunction( - node: T, - statement?: number = FUNC_NO_FLAGS, - isAsync?: boolean = false, + parseFunction( + this: Parser, + node: Undone, + statement: number = FUNC_NO_FLAGS, + isAsync: boolean = false, ): T { const isStatement = statement & FUNC_STATEMENT; const isHangingStatement = statement & FUNC_HANGING_STATEMENT; @@ -1300,6 +1381,7 @@ export default class StatementParser extends ExpressionParser { // Parse the function body. this.parseFunctionBodyAndFinish( node, + // @ts-expect-error node must be one of types isStatement ? "FunctionDeclaration" : "FunctionExpression", ); }); @@ -1311,20 +1393,24 @@ export default class StatementParser extends ExpressionParser { // We need to register this _after_ parsing the function body // because of TypeScript body-less function declarations, // which shouldn't be added to the scope. - this.registerFunctionStatementId(node); + this.registerFunctionStatementId(node as T); } this.state.maybeInArrowParameters = oldMaybeInArrowParameters; - return node; + return node as T; } - parseFunctionId(requireId?: boolean): ?N.Identifier { + parseFunctionId(requireId?: boolean): N.Identifier | undefined | null { return requireId || tokenIsIdentifier(this.state.type) ? this.parseIdentifier() : null; } - parseFunctionParams(node: N.Function, allowModifiers?: boolean): void { + parseFunctionParams( + this: Parser, + node: Undone, + allowModifiers?: boolean, + ): void { this.expect(tt.parenL); this.expressionScope.enter(newParameterDeclarationScope()); node.params = this.parseBindingList( @@ -1358,8 +1444,9 @@ export default class StatementParser extends ExpressionParser { // Parse a class declaration or literal (depending on the // `isStatement` parameter). - parseClass( - node: T, + parseClass( + this: Parser, + node: Undone, isStatement: /* T === ClassDeclaration */ boolean, optionalId?: boolean, ): T { @@ -1399,7 +1486,11 @@ export default class StatementParser extends ExpressionParser { } // https://tc39.es/ecma262/#prod-ClassBody - parseClassBody(hadSuperClass: boolean, oldStrict: boolean): N.ClassBody { + parseClassBody( + this: Parser, + hadSuperClass: boolean, + oldStrict: boolean, + ): N.ClassBody { this.classScope.enter(); const state: N.ParseClassMemberState = { @@ -1407,7 +1498,7 @@ export default class StatementParser extends ExpressionParser { hadSuperClass, }; let decorators: N.Decorator[] = []; - const classBody: N.ClassBody = this.startNode(); + const classBody = this.startNode(); classBody.body = []; this.expect(tt.braceL); @@ -1431,10 +1522,11 @@ export default class StatementParser extends ExpressionParser { continue; } - const member = this.startNode(); + const member = this.startNode(); // steal the decorators if there are any if (decorators.length) { + // @ts-expect-error Fixme member.decorators = decorators; this.resetStartLocationFromNode(member, decorators[0]); decorators = []; @@ -1443,8 +1535,11 @@ export default class StatementParser extends ExpressionParser { this.parseClassMember(classBody, member, state); if ( + // @ts-expect-error Fixme member.kind === "constructor" && + // @ts-expect-error Fixme member.decorators && + // @ts-expect-error Fixme member.decorators.length > 0 ) { this.raise(Errors.DecoratorConstructor, { at: member }); @@ -1468,13 +1563,14 @@ export default class StatementParser extends ExpressionParser { // returns true if the current identifier is a method/field name, // false if it is a modifier parseClassMemberFromModifier( - classBody: N.ClassBody, - member: N.ClassMember, + this: Parser, + classBody: Undone, + member: Undone, ): boolean { const key = this.parseIdentifier(true); // eats the modifier if (this.isClassMethod()) { - const method: N.ClassMethod = (member: any); + const method: N.ClassMethod = member as any; // a method named like the modifier method.kind = "method"; @@ -1491,7 +1587,7 @@ export default class StatementParser extends ExpressionParser { ); return true; } else if (this.isClassProperty()) { - const prop: N.ClassProperty = (member: any); + const prop: N.ClassProperty = member as any; // a property named like the modifier prop.computed = false; @@ -1505,8 +1601,9 @@ export default class StatementParser extends ExpressionParser { } parseClassMember( - classBody: N.ClassBody, - member: N.ClassMember, + this: Parser, + classBody: Undone, + member: Undone, state: N.ParseClassMemberState, ): void { const isStatic = this.isContextual(tt._static); @@ -1517,7 +1614,7 @@ export default class StatementParser extends ExpressionParser { return; } if (this.eat(tt.braceL)) { - this.parseClassStaticBlock(classBody, ((member: any): N.StaticBlock)); + this.parseClassStaticBlock(classBody, member as any as N.StaticBlock); return; } } @@ -1526,15 +1623,21 @@ export default class StatementParser extends ExpressionParser { } parseClassMemberWithIsStatic( - classBody: N.ClassBody, - member: N.ClassMember, + this: Parser, + classBody: Undone, + member: Undone, state: N.ParseClassMemberState, isStatic: boolean, ) { + // @ts-expect-error: Fixme: convert $FlowSubtype to TS const publicMethod: $FlowSubtype = member; + // @ts-expect-error: Fixme: convert $FlowSubtype to TS const privateMethod: $FlowSubtype = member; + // @ts-expect-error: Fixme: convert $FlowSubtype to TS const publicProp: $FlowSubtype = member; + // @ts-expect-error: Fixme: convert $FlowSubtype to TS const privateProp: $FlowSubtype = member; + // @ts-expect-error: Fixme: convert $FlowSubtype to TS const accessorProp: $FlowSubtype = member; const method: typeof publicMethod | typeof privateMethod = publicMethod; @@ -1717,7 +1820,10 @@ export default class StatementParser extends ExpressionParser { } // https://tc39.es/ecma262/#prod-ClassElementName - parseClassElementName(member: N.ClassMember): N.Expression | N.Identifier { + parseClassElementName( + this: Parser, + member: Undone, + ): N.Expression | N.Identifier { const { type, value } = this.state; if ( (type === tt.name || type === tt.string) && @@ -1742,8 +1848,13 @@ export default class StatementParser extends ExpressionParser { } parseClassStaticBlock( - classBody: N.ClassBody, - member: N.StaticBlock & { decorators?: Array }, + this: Parser, + classBody: Undone, + member: Undone< + N.StaticBlock & { + decorators?: Array; + } + >, ) { // Start a new lexical scope this.scope.enter(SCOPE_CLASS | SCOPE_STATIC_BLOCK | SCOPE_SUPER); @@ -1753,7 +1864,7 @@ export default class StatementParser extends ExpressionParser { // ClassStaticBlockStatementList: // StatementList[~Yield, ~Await, ~Return] opt this.prodParam.enter(PARAM); - const body = (member.body = []); + const body: N.Node[] = (member.body = []); this.parseBlockOrModuleBlockBody(body, undefined, false, tt.braceR); this.prodParam.exit(); this.scope.exit(); @@ -1764,7 +1875,11 @@ export default class StatementParser extends ExpressionParser { } } - pushClassProperty(classBody: N.ClassBody, prop: N.ClassProperty) { + pushClassProperty( + this: Parser, + classBody: Undone, + prop: N.ClassProperty, + ) { if ( !prop.computed && (prop.key.name === "constructor" || prop.key.value === "constructor") @@ -1778,8 +1893,9 @@ export default class StatementParser extends ExpressionParser { } pushClassPrivateProperty( - classBody: N.ClassBody, - prop: N.ClassPrivateProperty, + this: Parser, + classBody: Undone, + prop: Undone, ) { const node = this.parseClassPrivateProperty(prop); classBody.body.push(node); @@ -1792,13 +1908,14 @@ export default class StatementParser extends ExpressionParser { } pushClassAccessorProperty( - classBody: N.ClassBody, + this: Parser, + classBody: Undone, prop: N.ClassAccessorProperty, isPrivate: boolean, ) { if (!isPrivate && !prop.computed) { // Not private, so not node is not a PrivateName and we can safely cast - const key = (prop.key: N.Expression); + const key = prop.key as N.Expression; if (key.name === "constructor" || key.value === "constructor") { // Non-computed field, which is either an identifier named "constructor" @@ -1820,8 +1937,9 @@ export default class StatementParser extends ExpressionParser { } pushClassMethod( - classBody: N.ClassBody, - method: N.ClassMethod, + this: Parser, + classBody: Undone, + method: Undone, isGenerator: boolean, isAsync: boolean, isConstructor: boolean, @@ -1841,8 +1959,9 @@ export default class StatementParser extends ExpressionParser { } pushClassPrivateMethod( - classBody: N.ClassBody, - method: N.ClassPrivateMethod, + this: Parser, + classBody: Undone, + method: Undone, isGenerator: boolean, isAsync: boolean, ): void { @@ -1871,7 +1990,9 @@ export default class StatementParser extends ExpressionParser { } declareClassPrivateMethodInScope( - node: N.ClassPrivateMethod | N.EstreeMethodDefinition | N.TSDeclareMethod, + node: Undone< + N.ClassPrivateMethod | N.EstreeMethodDefinition | N.TSDeclareMethod + >, kind: number, ) { this.classScope.declarePrivateName( @@ -1883,13 +2004,14 @@ export default class StatementParser extends ExpressionParser { // Overridden in typescript.js parsePostMemberNameModifiers( - // eslint-disable-next-line no-unused-vars - methodOrProp: N.ClassMethod | N.ClassProperty, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + methodOrProp: Undone, ): void {} // https://tc39.es/ecma262/#prod-FieldDefinition parseClassPrivateProperty( - node: N.ClassPrivateProperty, + this: Parser, + node: Undone, ): N.ClassPrivateProperty { this.parseInitializer(node); this.semicolon(); @@ -1897,13 +2019,14 @@ export default class StatementParser extends ExpressionParser { } // https://tc39.es/ecma262/#prod-FieldDefinition - parseClassProperty(node: N.ClassProperty): N.ClassProperty { + parseClassProperty(this: Parser, node: N.ClassProperty): N.ClassProperty { this.parseInitializer(node); this.semicolon(); return this.finishNode(node, "ClassProperty"); } parseClassAccessorProperty( + this: Parser, node: N.ClassAccessorProperty, ): N.ClassAccessorProperty { this.parseInitializer(node); @@ -1913,7 +2036,10 @@ export default class StatementParser extends ExpressionParser { // https://tc39.es/ecma262/#prod-Initializer parseInitializer( - node: N.ClassProperty | N.ClassPrivateProperty | N.ClassAccessorProperty, + this: Parser, + node: Undone< + N.ClassProperty | N.ClassPrivateProperty | N.ClassAccessorProperty + >, ): void { this.scope.enter(SCOPE_CLASS | SCOPE_SUPER); this.expressionScope.enter(newExpressionScope()); @@ -1925,9 +2051,9 @@ export default class StatementParser extends ExpressionParser { } parseClassId( - node: N.Class, + node: Undone, isStatement: boolean, - optionalId: ?boolean, + optionalId?: boolean | null, bindingType: BindingTypes = BIND_CLASS, ): void { if (tokenIsIdentifier(this.state.type)) { @@ -1945,14 +2071,21 @@ export default class StatementParser extends ExpressionParser { } // https://tc39.es/ecma262/#prod-ClassHeritage - parseClassSuper(node: N.Class): void { + parseClassSuper(this: Parser, node: Undone): void { node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null; } // Parses module export declaration. // https://tc39.es/ecma262/#prod-ExportDeclaration - parseExport(node: N.Node): N.AnyExport { + parseExport( + this: Parser, + node: Undone< + | N.ExportDefaultDeclaration + | N.ExportAllDeclaration + | N.ExportNamedDeclaration + >, + ): N.AnyExport { const hasDefault = this.maybeParseExportDefaultSpecifier(node); const parseAfterDefault = !hasDefault || this.eat(tt.comma); const hasStar = parseAfterDefault && this.eatExportStar(node); @@ -1964,7 +2097,7 @@ export default class StatementParser extends ExpressionParser { if (hasStar && !hasNamespace) { if (hasDefault) this.unexpected(); - this.parseExportFrom(node, true); + this.parseExportFrom(node as Undone, true); return this.finishNode(node, "ExportAllDeclaration"); } @@ -1981,20 +2114,31 @@ export default class StatementParser extends ExpressionParser { let hasDeclaration; if (isFromRequired || hasSpecifiers) { hasDeclaration = false; - this.parseExportFrom(node, isFromRequired); + this.parseExportFrom( + node as Undone, + isFromRequired, + ); } else { - hasDeclaration = this.maybeParseExportDeclaration(node); + hasDeclaration = this.maybeParseExportDeclaration( + node as Undone, + ); } if (isFromRequired || hasSpecifiers || hasDeclaration) { - this.checkExport(node, true, false, !!node.source); + this.checkExport( + node as Undone, + true, + false, + !!(node as Undone).source, + ); return this.finishNode(node, "ExportNamedDeclaration"); } if (this.eat(tt._default)) { // export default ... - node.declaration = this.parseExportDefaultExpression(); - this.checkExport(node, true, true); + (node as Undone).declaration = + this.parseExportDefaultExpression(); + this.checkExport(node as Undone, true, true); return this.finishNode(node, "ExportDefaultDeclaration"); } @@ -2002,7 +2146,7 @@ export default class StatementParser extends ExpressionParser { throw this.unexpected(null, tt.braceL); } - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars eatExportStar(node: N.Node): boolean { return this.eat(tt.star); } @@ -2056,7 +2200,10 @@ export default class StatementParser extends ExpressionParser { return false; } - maybeParseExportDeclaration(node: N.Node): boolean { + maybeParseExportDeclaration( + this: Parser, + node: Undone, + ): boolean { if (this.shouldParseExportDeclaration()) { node.specifiers = []; node.source = null; @@ -2078,7 +2225,7 @@ export default class StatementParser extends ExpressionParser { ); } - parseExportDefaultExpression(): N.Expression | N.Declaration { + parseExportDefaultExpression(this: Parser): N.Expression | N.Declaration { const expr = this.startNode(); const isAsync = this.isAsyncFunction(); @@ -2090,14 +2237,14 @@ export default class StatementParser extends ExpressionParser { } return this.parseFunction( - expr, + expr as Undone, FUNC_STATEMENT | FUNC_NULLABLE_ID, isAsync, ); } if (this.match(tt._class)) { - return this.parseClass(expr, true, true); + return this.parseClass(expr as Undone, true, true); } if (this.match(tt.at)) { @@ -2108,7 +2255,7 @@ export default class StatementParser extends ExpressionParser { this.raise(Errors.DecoratorBeforeExport, { at: this.state.startLoc }); } this.parseDecorators(false); - return this.parseClass(expr, true, true); + return this.parseClass(expr as Undone, true, true); } if (this.match(tt._const) || this.match(tt._var) || this.isLet()) { @@ -2122,9 +2269,12 @@ export default class StatementParser extends ExpressionParser { return res; } - // eslint-disable-next-line no-unused-vars - parseExportDeclaration(node: N.ExportNamedDeclaration): ?N.Declaration { - return this.parseStatement(null); + parseExportDeclaration( + this: Parser, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + node: Undone, + ): N.Declaration | undefined | null { + return this.parseStatement(null) as N.Declaration; } isExportDefaultSpecifier(): boolean { @@ -2175,7 +2325,11 @@ export default class StatementParser extends ExpressionParser { return false; } - parseExportFrom(node: N.ExportNamedDeclaration, expect?: boolean): void { + parseExportFrom( + this: Parser, + node: Undone, + expect?: boolean, + ): void { if (this.eatContextual(tt._from)) { node.source = this.parseImportSource(); this.checkExport(node); @@ -2217,7 +2371,7 @@ export default class StatementParser extends ExpressionParser { } checkExport( - node: N.ExportNamedDeclaration, + node: Undone, checkNames?: boolean, isDefault?: boolean, isFrom?: boolean, @@ -2228,10 +2382,11 @@ export default class StatementParser extends ExpressionParser { // Default exports this.checkDuplicateExports(node, "default"); if (this.hasPlugin("exportDefaultFrom")) { - const declaration = ((node: any): N.ExportDefaultDeclaration) + const declaration = (node as any as N.ExportDefaultDeclaration) .declaration; if ( declaration.type === "Identifier" && + // @ts-expect-error migrate to Babel types declaration.name === "from" && declaration.end - declaration.start === 4 && // does not contain escape !declaration.extra?.parenthesized @@ -2241,14 +2396,15 @@ export default class StatementParser extends ExpressionParser { }); } } + // @ts-expect-error node.specifiers may not exist } else if (node.specifiers && node.specifiers.length) { // Named exports + // @ts-expect-error node.specifiers may not exist for (const specifier of node.specifiers) { const { exported } = specifier; const exportName = exported.type === "Identifier" ? exported.name : exported.value; this.checkDuplicateExports(specifier, exportName); - // $FlowIgnore if (!isFrom && specifier.local) { const { local } = specifier; if (local.type !== "Identifier") { @@ -2271,11 +2427,13 @@ export default class StatementParser extends ExpressionParser { node.declaration.type === "FunctionDeclaration" || node.declaration.type === "ClassDeclaration" ) { + // @ts-expect-error migrate to Babel types const id = node.declaration.id; if (!id) throw new Error("Assertion failure"); this.checkDuplicateExports(node, id.name); } else if (node.declaration.type === "VariableDeclaration") { + // @ts-expect-error migrate to Babel types for (const declaration of node.declaration.declarations) { this.checkDeclaration(declaration.id); } @@ -2306,6 +2464,7 @@ export default class StatementParser extends ExpressionParser { } } } else if (node.type === "ObjectProperty") { + // @ts-expect-error migrate to Babel types this.checkDeclaration(node.value); } else if (node.type === "RestElement") { this.checkDeclaration(node.argument); @@ -2315,12 +2474,13 @@ export default class StatementParser extends ExpressionParser { } checkDuplicateExports( - node: + node: Undone< | N.Identifier | N.StringLiteral | N.ExportNamedDeclaration | N.ExportSpecifier - | N.ExportDefaultSpecifier, + | N.ExportDefaultSpecifier + >, exportName: string, ): void { if (this.exportedIdentifiers.has(exportName)) { @@ -2369,10 +2529,10 @@ export default class StatementParser extends ExpressionParser { parseExportSpecifier( node: any, isString: boolean, - /* eslint-disable no-unused-vars -- used in TypeScript parser */ + /* eslint-disable @typescript-eslint/no-unused-vars -- used in TypeScript parser */ isInTypeExport: boolean, isMaybeTypeOnly: boolean, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ ): N.ExportSpecifier { if (this.eatContextual(tt._as)) { node.exported = this.parseModuleExportName(); @@ -2401,10 +2561,9 @@ export default class StatementParser extends ExpressionParser { } isJSONModuleImport( - node: - | N.ExportAllDeclaration - | N.ExportNamedDeclaration - | N.ImportDeclaration, + node: Undone< + N.ExportAllDeclaration | N.ExportNamedDeclaration | N.ImportDeclaration + >, ): boolean { if (node.assertions != null) { return node.assertions.some(({ key, value }) => { @@ -2420,14 +2579,17 @@ export default class StatementParser extends ExpressionParser { } checkJSONModuleImport( - node: - | N.ExportAllDeclaration - | N.ExportNamedDeclaration - | N.ImportDeclaration, + node: Undone< + N.ExportAllDeclaration | N.ExportNamedDeclaration | N.ImportDeclaration + >, ) { + // @ts-expect-error Fixme: node.type must be undefined because they are undone if (this.isJSONModuleImport(node) && node.type !== "ExportAllDeclaration") { + // @ts-expect-error const { specifiers } = node; + // @ts-expect-error if (node.specifiers != null) { + // @ts-expect-error refine specifier types const nonDefaultNamedSpecifier = specifiers.find(specifier => { let imported; if (specifier.type === "ExportSpecifier") { @@ -2453,7 +2615,7 @@ export default class StatementParser extends ExpressionParser { // Parses import declaration. // https://tc39.es/ecma262/#prod-ImportDeclaration - parseImport(node: N.Node): N.AnyImport { + parseImport(this: Parser, node: Undone): N.AnyImport { // import '...' node.specifiers = []; if (!this.match(tt.string)) { @@ -2485,6 +2647,7 @@ export default class StatementParser extends ExpressionParser { } else if (!process.env.BABEL_8_BREAKING) { const attributes = this.maybeParseModuleAttributes(); if (attributes) { + // @ts-expect-error attributes have been deprecated node.attributes = attributes; } } @@ -2494,27 +2657,38 @@ export default class StatementParser extends ExpressionParser { return this.finishNode(node, "ImportDeclaration"); } - parseImportSource(): N.StringLiteral { + parseImportSource(this: Parser): N.StringLiteral { if (!this.match(tt.string)) this.unexpected(); - return this.parseExprAtom(); + return this.parseExprAtom() as N.StringLiteral; } - // eslint-disable-next-line no-unused-vars - shouldParseDefaultImport(node: N.ImportDeclaration): boolean { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + shouldParseDefaultImport(node: Undone): boolean { return tokenIsIdentifier(this.state.type); } - parseImportSpecifierLocal( - node: N.ImportDeclaration, - specifier: N.Node, - type: string, + parseImportSpecifierLocal< + T extends + | N.ImportSpecifier + | N.ImportDefaultSpecifier + | N.ImportNamespaceSpecifier, + >( + node: Undone, + specifier: Undone, + type: T["type"], ): void { specifier.local = this.parseIdentifier(); node.specifiers.push(this.finishImportSpecifier(specifier, type)); } - finishImportSpecifier(specifier: N.Node, type: string) { + finishImportSpecifier< + T extends + | N.ImportSpecifier + | N.ImportDefaultSpecifier + | N.ImportNamespaceSpecifier, + >(specifier: Undone, type: T["type"]) { this.checkLVal(specifier.local, { + // @ts-expect-error refine types in: specifier, binding: BIND_LEXICAL, }); @@ -2564,8 +2738,7 @@ export default class StatementParser extends ExpressionParser { }); } node.value = this.parseStringLiteral(this.state.value); - this.finishNode(node, "ImportAttribute"); - attrs.push(node); + attrs.push(this.finishNode(node, "ImportAttribute")); } while (this.eat(tt.comma)); return attrs; @@ -2635,12 +2808,12 @@ export default class StatementParser extends ExpressionParser { return attrs; } - maybeParseDefaultImportSpecifier(node: N.ImportDeclaration): boolean { + maybeParseDefaultImportSpecifier(node: Undone): boolean { if (this.shouldParseDefaultImport(node)) { // import defaultObj, { x, y as z } from '...' this.parseImportSpecifierLocal( node, - this.startNode(), + this.startNode(), "ImportDefaultSpecifier", ); return true; @@ -2648,9 +2821,9 @@ export default class StatementParser extends ExpressionParser { return false; } - maybeParseStarImportSpecifier(node: N.ImportDeclaration): boolean { + maybeParseStarImportSpecifier(node: Undone): boolean { if (this.match(tt.star)) { - const specifier = this.startNode(); + const specifier = this.startNode(); this.next(); this.expectContextual(tt._as); @@ -2664,7 +2837,7 @@ export default class StatementParser extends ExpressionParser { return false; } - parseNamedImportSpecifiers(node: N.ImportDeclaration) { + parseNamedImportSpecifiers(node: Undone) { let first = true; this.expect(tt.braceL); while (!this.eat(tt.braceR)) { @@ -2682,7 +2855,7 @@ export default class StatementParser extends ExpressionParser { if (this.eat(tt.braceR)) break; } - const specifier = this.startNode(); + const specifier = this.startNode(); const importedIsString = this.match(tt.string); const isMaybeTypeOnly = this.isContextual(tt._type); specifier.imported = this.parseModuleExportName(); @@ -2698,12 +2871,12 @@ export default class StatementParser extends ExpressionParser { // https://tc39.es/ecma262/#prod-ImportSpecifier parseImportSpecifier( - specifier: any, + specifier: Undone, importedIsString: boolean, - /* eslint-disable no-unused-vars -- used in TypeScript and Flow parser */ + /* eslint-disable @typescript-eslint/no-unused-vars -- used in TypeScript and Flow parser */ isInTypeOnlyImport: boolean, isMaybeTypeOnly: boolean, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ ): N.ImportSpecifier { if (this.eatContextual(tt._as)) { specifier.local = this.parseIdentifier(); @@ -2712,10 +2885,15 @@ export default class StatementParser extends ExpressionParser { if (importedIsString) { throw this.raise(Errors.ImportBindingIsString, { at: specifier, - importName: imported.value, + importName: (imported as N.StringLiteral).value, }); } - this.checkReservedWord(imported.name, specifier.loc.start, true, true); + this.checkReservedWord( + (imported as N.Identifier).name, + specifier.loc.start, + true, + true, + ); if (!specifier.local) { specifier.local = cloneIdentifier(imported); } diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.ts similarity index 91% rename from packages/babel-parser/src/parser/util.js rename to packages/babel-parser/src/parser/util.ts index fbe51edefa16..50688232af4f 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.ts @@ -1,5 +1,3 @@ -// @flow - import { type Position } from "../util/location"; import { tokenIsLiteralPropertyName, @@ -8,7 +6,7 @@ import { } from "../tokenizer/types"; import Tokenizer from "../tokenizer"; import State from "../tokenizer/state"; -import type { Node } from "../types"; +import type { EstreePropertyDefinition, Node, ObjectProperty } from "../types"; import { lineBreak, skipWhiteSpaceToLineBreak } from "../util/whitespace"; import { isIdentifierChar } from "../util/identifier"; import ClassScopeHandler from "../util/class-scope"; @@ -23,16 +21,18 @@ import { type ParseError, type ParseErrorConstructor, } from "../parse-error"; +import type Parser from "."; + /*:: import type ScopeHandler from "../util/scope"; */ type TryParse = { - node: Node, - error: Error, - thrown: Thrown, - aborted: Aborted, - failState: FailState, + node: Node; + error: Error; + thrown: Thrown; + aborted: Aborted; + failState: FailState; }; // ## Parser utilities @@ -149,20 +149,22 @@ export default class UtilParser extends Tokenizer { // Expect a token of a given type. If found, consume it, otherwise, // raise an unexpected token error at given pos. - expect(type: TokenType, loc?: ?Position): void { + expect(type: TokenType, loc?: Position | null): void { this.eat(type) || this.unexpected(loc, type); } // tryParse will clone parser state. // It is expensive and should be used with cautions - tryParse>( - fn: (abort: (node?: T) => empty) => T, + tryParse>( + fn: (abort: (node?: T) => never) => T, oldState: State = this.state.clone(), ): | TryParse | TryParse, boolean, false, State> | TryParse { - const abortSignal: { node: T | null } = { node: null }; + const abortSignal: { + node: T | null; + } = { node: null }; try { const node = fn((node = null) => { abortSignal.node = node; @@ -177,7 +179,7 @@ export default class UtilParser extends Tokenizer { this.state.tokensLength = failState.tokensLength; return { node, - error: (failState.errors[oldState.errors.length]: ParseError), + error: failState.errors[oldState.errors.length] as ParseError, thrown: false, aborted: false, failState, @@ -195,6 +197,7 @@ export default class UtilParser extends Tokenizer { const failState = this.state; this.state = oldState; if (error instanceof SyntaxError) { + // @ts-expect-error casting general syntax error to parse error return { node: null, error, thrown: true, aborted: false, failState }; } if (error === abortSignal) { @@ -212,7 +215,7 @@ export default class UtilParser extends Tokenizer { } checkExpressionErrors( - refExpressionErrors: ?ExpressionErrors, + refExpressionErrors: ExpressionErrors | undefined | null, andThrow: boolean, ) { if (!refExpressionErrors) return false; @@ -302,7 +305,9 @@ export default class UtilParser extends Tokenizer { ); } - isObjectProperty(node: Node): boolean { + isObjectProperty( + node: Node, + ): node is ObjectProperty | EstreePropertyDefinition { return node.type === "ObjectProperty"; } @@ -311,6 +316,7 @@ export default class UtilParser extends Tokenizer { } initializeScopes( + this: Parser, inModule: boolean = this.options.sourceType === "module", ): () => void { // Initialize state @@ -382,8 +388,8 @@ export default class UtilParser extends Tokenizer { * It's only used by typescript and flow plugins */ export class ExpressionErrors { - shorthandAssignLoc: ?Position = null; - doubleProtoLoc: ?Position = null; - privateKeyLoc: ?Position = null; - optionalParametersLoc: ?Position = null; + shorthandAssignLoc: Position | undefined | null = null; + doubleProtoLoc: Position | undefined | null = null; + privateKeyLoc: Position | undefined | null = null; + optionalParametersLoc: Position | undefined | null = null; } diff --git a/packages/babel-parser/src/plugin-utils.js b/packages/babel-parser/src/plugin-utils.ts similarity index 92% rename from packages/babel-parser/src/plugin-utils.js rename to packages/babel-parser/src/plugin-utils.ts index f663d5552e2d..b2d1cc17bf9c 100644 --- a/packages/babel-parser/src/plugin-utils.js +++ b/packages/babel-parser/src/plugin-utils.ts @@ -1,13 +1,13 @@ -// @flow - import type Parser from "./parser"; -import type { PluginConfig } from "./parser/base"; +import type { PluginConfig } from "./typings"; export type Plugin = PluginConfig; -export type PluginList = $ReadOnlyArray; +export type PluginList = PluginConfig[]; -export type MixinPlugin = (superClass: Class) => Class; +export type MixinPlugin = (superClass: { new (...args: any): Parser }) => { + new (...args: any): Parser; +}; // This function’s second parameter accepts either a string (plugin name) or an // array pair (plugin name and options object). If an options object is given, @@ -37,6 +37,7 @@ export function hasPlugin( return false; } for (const key of expectedKeys) { + // @ts-expect-error key may not exist in plugin options if (pluginOptions[key] !== expectedOptions[key]) { return false; } @@ -60,6 +61,7 @@ export function getPluginOption( }); if (plugin && Array.isArray(plugin)) { + // @ts-expect-error Fixme: should check whether option is defined return plugin[1][option]; } @@ -202,7 +204,7 @@ export function validatePlugins(plugins: PluginList) { const error = new Error( "'asyncDoExpressions' requires 'doExpressions', please add 'doExpressions' to parser plugins.", ); - // $FlowIgnore + // @ts-expect-error error.missingPlugins = "doExpressions"; // so @babel/core can provide better error message throw error; } @@ -218,7 +220,9 @@ import placeholders from "./plugins/placeholders"; import v8intrinsic from "./plugins/v8intrinsic"; // NOTE: order is important. estree must come first; placeholders must come last. -export const mixinPlugins: { [name: string]: MixinPlugin } = { +export const mixinPlugins: { + [name: string]: MixinPlugin; +} = { estree, jsx, flow, @@ -227,5 +231,6 @@ export const mixinPlugins: { [name: string]: MixinPlugin } = { placeholders, }; -export const mixinPluginNames: $ReadOnlyArray = - Object.keys(mixinPlugins); +export const mixinPluginNames = Object.keys(mixinPlugins) as ReadonlyArray< + "estree" | "jsx" | "flow" | "typescript" | "v8intrinsic" | "placeholders" +>; diff --git a/packages/babel-parser/src/plugins/estree.js b/packages/babel-parser/src/plugins/estree.ts similarity index 68% rename from packages/babel-parser/src/plugins/estree.js rename to packages/babel-parser/src/plugins/estree.ts index 286218741487..8751d65d68b2 100644 --- a/packages/babel-parser/src/plugins/estree.js +++ b/packages/babel-parser/src/plugins/estree.ts @@ -1,5 +1,3 @@ -// @flow - import { type TokenType } from "../tokenizer/types"; import type Parser from "../parser"; import type { ExpressionErrors } from "../parser/util"; @@ -7,9 +5,11 @@ import * as N from "../types"; import type { Node as NodeType, NodeBase, File } from "../types"; import type { Position } from "../util/location"; import { Errors } from "../parse-error"; +import type { Undone } from "../parser/node"; +import type { BindingTypes } from "../util/scopeflags"; const { defineProperty } = Object; -const toUnenumerable = (object, key) => +const toUnenumerable = (object: any, key: string) => defineProperty(object, key, { enumerable: false, value: object[key] }); function toESTreeLocation(node: any) { @@ -19,7 +19,12 @@ function toESTreeLocation(node: any) { return node; } -export default (superClass: Class): Class => +export default (superClass: { + new (...args: any): Parser; +}): { + new (...args: any): Parser; +} => + // @ts-expect-error plugin may override interfaces class extends superClass { parse(): File { const file = toESTreeLocation(super.parse()); @@ -31,8 +36,9 @@ export default (superClass: Class): Class => return file; } - parseRegExpLiteral({ pattern, flags }): N.Node { - let regex = null; + // @ts-expect-error ESTree plugin changes node types + parseRegExpLiteral({ pattern, flags }): N.EstreeRegExpLiteral { + let regex: RegExp | null = null; try { regex = new RegExp(pattern, flags); } catch (e) { @@ -45,11 +51,11 @@ export default (superClass: Class): Class => return node; } + // @ts-expect-error ESTree plugin changes node types parseBigIntLiteral(value: any): N.Node { // https://github.com/estree/estree/blob/master/es2020.md#bigintliteral - let bigInt; + let bigInt: BigInt | null; try { - // $FlowIgnore bigInt = BigInt(value); } catch { bigInt = null; @@ -60,20 +66,23 @@ export default (superClass: Class): Class => return node; } + // @ts-expect-error ESTree plugin changes node types parseDecimalLiteral(value: any): N.Node { // https://github.com/estree/estree/blob/master/experimental/decimal.md // todo: use BigDecimal when node supports it. - const decimal = null; + const decimal: null = null; const node = this.estreeParseLiteral(decimal); node.decimal = String(node.value || value); return node; } - estreeParseLiteral(value: any) { + estreeParseLiteral(value: any) { + // @ts-expect-error ESTree plugin changes node types return this.parseLiteral(value, "Literal"); } + // @ts-expect-error ESTree plugin changes node types parseStringLiteral(value: any): N.Node { return this.estreeParseLiteral(value); } @@ -82,6 +91,7 @@ export default (superClass: Class): Class => return this.estreeParseLiteral(value); } + // @ts-expect-error ESTree plugin changes node types parseNullLiteral(): N.Node { return this.estreeParseLiteral(null); } @@ -93,20 +103,29 @@ export default (superClass: Class): Class => directiveToStmt(directive: N.Directive): N.ExpressionStatement { const directiveLiteral = directive.value; - const stmt = this.startNodeAt(directive.start, directive.loc.start); - const expression = this.startNodeAt( + const stmt = this.startNodeAt( + directive.start, + directive.loc.start, + ); + const expression = this.startNodeAt( + // @ts-expect-error N.Directive.value is not defined directiveLiteral.start, + // @ts-expect-error N.Directive.value is not defined directiveLiteral.loc.start, ); + // @ts-expect-error N.Directive.value is not defined expression.value = directiveLiteral.extra.expressionValue; + // @ts-expect-error N.Directive.value is not defined expression.raw = directiveLiteral.extra.raw; stmt.expression = this.finishNodeAt( expression, "Literal", + // @ts-expect-error N.Directive.value is not defined directiveLiteral.loc.end, ); + // @ts-expect-error N.Directive.value is not defined stmt.directive = directiveLiteral.extra.raw.slice(1, -1); return this.finishNodeAt(stmt, "ExpressionStatement", directive.loc.end); @@ -118,7 +137,7 @@ export default (superClass: Class): Class => initFunction( node: N.BodilessFunctionOrMethodBase, - isAsync: ?boolean, + isAsync?: boolean | null, ): void { super.initFunction(node, isAsync); node.expression = false; @@ -126,15 +145,16 @@ export default (superClass: Class): Class => checkDeclaration(node: N.Pattern | N.ObjectProperty): void { if (node != null && this.isObjectProperty(node)) { - this.checkDeclaration(((node: any): N.EstreeProperty).value); + // @ts-expect-error plugin typings + this.checkDeclaration((node as unknown as N.EstreeProperty).value); } else { super.checkDeclaration(node); } } getObjectOrClassMethodParams(method: N.ObjectMethod | N.ClassMethod) { - return ((method: any): N.EstreeProperty | N.EstreeMethodDefinition).value - .params; + return (method as any as N.EstreeProperty | N.EstreeMethodDefinition) + .value.params; } isValidDirective(stmt: N.Statement): boolean { @@ -148,15 +168,21 @@ export default (superClass: Class): Class => parseBlockBody( node: N.BlockStatementLike, - ...args: [?boolean, boolean, TokenType, void | (boolean => void)] + ...args: [ + boolean | undefined | null, + boolean, + TokenType, + void | ((a: boolean) => void), + ] ): void { + // @ts-expect-error figure out args typings super.parseBlockBody(node, ...args); const directiveStatements = node.directives.map(d => this.directiveToStmt(d), ); + // @ts-expect-error estree plugin typings node.body = directiveStatements.concat(node.body); - // $FlowIgnore - directives isn't optional in the type definition delete node.directives; } @@ -178,7 +204,7 @@ export default (superClass: Class): Class => true, ); if (method.typeParameters) { - // $FlowIgnore + // @ts-expect-error method.value.typeParameters = method.typeParameters; delete method.typeParameters; } @@ -199,11 +225,13 @@ export default (superClass: Class): Class => node: N.PrivateName, ): N.EstreePrivateIdentifier { const name = super.getPrivateNameSV(node); - node = (node: any); + node = node as any; delete node.id; + // @ts-expect-error mutate AST types node.name = name; + // @ts-expect-error mutate AST types node.type = "PrivateIdentifier"; - return node; + return node as unknown as N.EstreePrivateIdentifier; } isPrivateName(node: N.Node): boolean { @@ -224,8 +252,10 @@ export default (superClass: Class): Class => return node.name; } - parseLiteral(value: any, type: $ElementType): T { + // @ts-expect-error plugin may override interfaces + parseLiteral(value: any, type: T["type"]): T { const node = super.parseLiteral(value, type); + // @ts-expect-error mutating AST types node.raw = node.extra.raw; delete node.extra; @@ -234,23 +264,26 @@ export default (superClass: Class): Class => parseFunctionBody( node: N.Function, - allowExpression: ?boolean, - isMethod?: boolean = false, + allowExpression?: boolean | null, + isMethod: boolean = false, ): void { super.parseFunctionBody(node, allowExpression, isMethod); node.expression = node.body.type !== "BlockStatement"; } - parseMethod( - node: T, + // @ts-expect-error plugin may override interfaces + parseMethod< + T extends N.ClassPrivateMethod | N.ObjectMethod | N.ClassMethod, + >( + node: Undone, isGenerator: boolean, isAsync: boolean, isConstructor: boolean, allowDirectSuper: boolean, - type: string, + type: T["type"], inClassScope: boolean = false, - ): T { - let funcNode = this.startNode(); + ): N.EstreeMethodDefinition { + let funcNode = this.startNode(); funcNode.kind = node.kind; // provide kind, so super method correctly sets state funcNode = super.parseMethod( funcNode, @@ -261,39 +294,42 @@ export default (superClass: Class): Class => type, inClassScope, ); + // @ts-expect-error mutate AST types funcNode.type = "FunctionExpression"; delete funcNode.kind; - // $FlowIgnore + // @ts-expect-error node.value = funcNode; if (type === "ClassPrivateMethod") { - // $FlowIgnore node.computed = false; } - type = "MethodDefinition"; - return this.finishNode(node, type); + return this.finishNode( + // @ts-expect-error cast methods to estree types + node as Undone, + "MethodDefinition", + ); } parseClassProperty(...args: [N.ClassProperty]): any { - const propertyNode = (super.parseClassProperty(...args): any); + const propertyNode = super.parseClassProperty(...args) as any; if (!process.env.BABEL_8_BREAKING) { if (!this.getPluginOption("estree", "classFeatures")) { - return (propertyNode: N.EstreePropertyDefinition); + return propertyNode as N.EstreePropertyDefinition; } } propertyNode.type = "PropertyDefinition"; - return (propertyNode: N.EstreePropertyDefinition); + return propertyNode as N.EstreePropertyDefinition; } parseClassPrivateProperty(...args: [N.ClassPrivateProperty]): any { - const propertyNode = (super.parseClassPrivateProperty(...args): any); + const propertyNode = super.parseClassPrivateProperty(...args) as any; if (!process.env.BABEL_8_BREAKING) { if (!this.getPluginOption("estree", "classFeatures")) { - return (propertyNode: N.EstreePropertyDefinition); + return propertyNode as N.EstreePropertyDefinition; } } propertyNode.type = "PropertyDefinition"; propertyNode.computed = false; - return (propertyNode: N.EstreePropertyDefinition); + return propertyNode as N.EstreePropertyDefinition; } parseObjectMethod( @@ -302,49 +338,57 @@ export default (superClass: Class): Class => isAsync: boolean, isPattern: boolean, isAccessor: boolean, - ): ?N.ObjectMethod { - const node: N.EstreeProperty = (super.parseObjectMethod( + ): N.ObjectMethod | undefined | null { + const node: N.EstreeProperty = super.parseObjectMethod( prop, isGenerator, isAsync, isPattern, isAccessor, - ): any); + ) as any; if (node) { node.type = "Property"; - if (((node: any): N.ClassMethod).kind === "method") node.kind = "init"; + if ((node as any as N.ClassMethod).kind === "method") { + node.kind = "init"; + } node.shorthand = false; } - return (node: any); + return node as any; } parseObjectProperty( prop: N.ObjectProperty, - startPos: ?number, - startLoc: ?Position, + startPos: number | undefined | null, + startLoc: Position | undefined | null, isPattern: boolean, - refExpressionErrors: ?ExpressionErrors, - ): ?N.ObjectProperty { - const node: N.EstreeProperty = (super.parseObjectProperty( + refExpressionErrors?: ExpressionErrors | null, + ): N.ObjectProperty | undefined | null { + const node: N.EstreeProperty = super.parseObjectProperty( prop, startPos, startLoc, isPattern, refExpressionErrors, - ): any); + ) as any; if (node) { node.kind = "init"; node.type = "Property"; } - return (node: any); + return node as any; } - isValidLVal(type: string, ...rest) { - return type === "Property" ? "value" : super.isValidLVal(type, ...rest); + isValidLVal( + type: string, + isUnparenthesizedInAssign: boolean, + binding: BindingTypes, + ) { + return type === "Property" + ? "value" + : super.isValidLVal(type, isUnparenthesizedInAssign, binding); } isAssignable(node: N.Node, isBinding?: boolean): boolean { @@ -369,32 +413,36 @@ export default (superClass: Class): Class => } } - toAssignableObjectExpressionProp(prop: N.Node) { + toAssignableObjectExpressionProp( + prop: N.Node, + isLast: boolean, + isLHS: boolean, + ) { if (prop.kind === "get" || prop.kind === "set") { this.raise(Errors.PatternHasAccessor, { at: prop.key }); } else if (prop.method) { this.raise(Errors.PatternHasMethod, { at: prop.key }); } else { - super.toAssignableObjectExpressionProp(...arguments); + super.toAssignableObjectExpressionProp(prop, isLast, isLHS); } } - finishCallExpression( - node: T, + finishCallExpression( + unfinished: Undone, optional: boolean, - ): N.Expression { - super.finishCallExpression(node, optional); + ): T { + const node = super.finishCallExpression(unfinished, optional); if (node.callee.type === "Import") { - ((node: N.Node): N.EstreeImportExpression).type = "ImportExpression"; - ((node: N.Node): N.EstreeImportExpression).source = node.arguments[0]; + (node as N.Node as N.EstreeImportExpression).type = "ImportExpression"; + (node as N.Node as N.EstreeImportExpression).source = node.arguments[0]; if (this.hasPlugin("importAssertions")) { - ((node: N.Node): N.EstreeImportExpression).attributes = + (node as N.Node as N.EstreeImportExpression).attributes = node.arguments[1] ?? null; } - // $FlowIgnore - arguments isn't optional in the type definition + // arguments isn't optional in the type definition delete node.arguments; - // $FlowIgnore - callee isn't optional in the type definition + // callee isn't optional in the type definition delete node.callee; } @@ -416,20 +464,24 @@ export default (superClass: Class): Class => super.toReferencedArguments(node); } - parseExport(node: N.Node) { - super.parseExport(node); + parseExport(unfinished: Undone) { + const node = super.parseExport(unfinished); switch (node.type) { case "ExportAllDeclaration": + // @ts-expect-error mutating AST types node.exported = null; break; case "ExportNamedDeclaration": if ( node.specifiers.length === 1 && + // @ts-expect-error mutating AST types node.specifiers[0].type === "ExportNamespaceSpecifier" ) { + // @ts-expect-error mutating AST types node.type = "ExportAllDeclaration"; + // @ts-expect-error mutating AST types node.exported = node.specifiers[0].exported; delete node.specifiers; } @@ -444,7 +496,7 @@ export default (superClass: Class): Class => base: N.Expression, startPos: number, startLoc: Position, - noCalls: ?boolean, + noCalls: boolean | undefined | null, state: N.ParseSubscriptState, ) { const node = super.parseSubscript( @@ -489,6 +541,7 @@ export default (superClass: Class): Class => return node.type === "ChainExpression"; } + // @ts-expect-error override interfaces isObjectProperty(node: N.Node): boolean { return node.type === "Property" && node.kind === "init" && !node.method; } @@ -497,7 +550,11 @@ export default (superClass: Class): Class => return node.method || node.kind === "get" || node.kind === "set"; } - finishNodeAt(node: T, type: string, endLoc: Position): T { + finishNodeAt( + node: Undone, + type: T["type"], + endLoc: Position, + ): T { return toESTreeLocation(super.finishNodeAt(node, type, endLoc)); } @@ -508,7 +565,7 @@ export default (superClass: Class): Class => resetEndLocation( node: NodeBase, - endLoc?: Position = this.state.lastTokEndLoc, + endLoc: Position = this.state.lastTokEndLoc, ): void { super.resetEndLocation(node, endLoc); toESTreeLocation(node); diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.ts similarity index 82% rename from packages/babel-parser/src/plugins/flow/index.js rename to packages/babel-parser/src/plugins/flow/index.ts index 9cc648fe0fe9..9319934b8aa2 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.ts @@ -1,5 +1,3 @@ -// @flow - /*:: declare var invariant; */ import type Parser from "../../parser"; @@ -27,10 +25,15 @@ import { SCOPE_ARROW, SCOPE_FUNCTION, SCOPE_OTHER, + type BindingTypes, } from "../../util/scopeflags"; import type { ExpressionErrors } from "../../parser/util"; -import { Errors, ParseErrorEnum } from "../../parse-error"; -import { cloneIdentifier } from "../../parser/node"; +import { + Errors, + ParseErrorEnum, + toParseErrorCredentials, +} from "../../parse-error"; +import { cloneIdentifier, type Undone } from "../../parser/node"; const reservedTypes = new Set([ "_", @@ -53,191 +56,204 @@ const reservedTypes = new Set([ /* eslint sort-keys: "error" */ // The Errors key follows https://github.com/facebook/flow/blob/master/src/parser/parse_error.ml unless it does not exist -const FlowErrors = ParseErrorEnum`flow`(_ => ({ - AmbiguousConditionalArrow: _( - "Ambiguous expression: wrap the arrow functions in parentheses to disambiguate.", - ), - AmbiguousDeclareModuleKind: _( - "Found both `declare module.exports` and `declare export` in the same module. Modules can only have 1 since they are either an ES module or they are a CommonJS module.", - ), - // TODO: When we get proper string enums in typescript make this ReservedType. - // Not really worth it to do the whole $Values dance with reservedTypes set. - AssignReservedType: _<{| reservedType: string |}>( - ({ reservedType }) => `Cannot overwrite reserved type ${reservedType}.`, - ), - DeclareClassElement: _( - "The `declare` modifier can only appear on class fields.", - ), - DeclareClassFieldInitializer: _( - "Initializers are not allowed in fields with the `declare` modifier.", - ), - DuplicateDeclareModuleExports: _( - "Duplicate `declare module.exports` statement.", - ), - EnumBooleanMemberNotInitialized: _<{| - memberName: string, - enumName: string, - |}>( - ({ memberName, enumName }) => - `Boolean enum members need to be initialized. Use either \`${memberName} = true,\` or \`${memberName} = false,\` in enum \`${enumName}\`.`, - ), - EnumDuplicateMemberName: _<{| memberName: string, enumName: string |}>( - ({ memberName, enumName }) => - `Enum member names need to be unique, but the name \`${memberName}\` has already been used before in enum \`${enumName}\`.`, - ), - EnumInconsistentMemberValues: _<{| enumName: string |}>( - ({ enumName }) => - `Enum \`${enumName}\` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers.`, - ), - EnumInvalidExplicitType: _<{| invalidEnumType: string, enumName: string |}>( - ({ invalidEnumType, enumName }) => - `Enum type \`${invalidEnumType}\` is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.`, - ), - EnumInvalidExplicitTypeUnknownSupplied: _<{| enumName: string |}>( - ({ enumName }) => - `Supplied enum type is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.`, - ), - - // TODO: When moving to typescript, we should either have each of the - // following errors only accept the specific strings they want: - // - // ...PrimaryType: explicitType: "string" | "number" | "boolean" - // ...SymbolType: explicitType: "symbol" - // ...UnknownType: explicitType: null - // - // Or, alternatively, merge these three errors together into one - // `EnumInvalidMemberInitializer` error that can accept `EnumExplicitType` - // without alteration, and then just have its message change based on the - // explicitType. - EnumInvalidMemberInitializerPrimaryType: _<{| - enumName: string, - memberName: string, - explicitType: EnumExplicitType, - |}>( - ({ enumName, memberName, explicitType }) => - // $FlowIgnore (coercing null which never actually happens) - `Enum \`${enumName}\` has type \`${explicitType}\`, so the initializer of \`${memberName}\` needs to be a ${explicitType} literal.`, - ), - EnumInvalidMemberInitializerSymbolType: _<{| - enumName: string, - memberName: string, - explicitType: EnumExplicitType, - |}>( - ({ enumName, memberName }) => - `Symbol enum members cannot be initialized. Use \`${memberName},\` in enum \`${enumName}\`.`, - ), - EnumInvalidMemberInitializerUnknownType: _<{| - enumName: string, - memberName: string, - explicitType: EnumExplicitType, - |}>( - ({ enumName, memberName }) => - `The enum member initializer for \`${memberName}\` needs to be a literal (either a boolean, number, or string) in enum \`${enumName}\`.`, - ), - EnumInvalidMemberName: _<{| - enumName: string, - memberName: string, - suggestion: string, - |}>( - ({ enumName, memberName, suggestion }) => - `Enum member names cannot start with lowercase 'a' through 'z'. Instead of using \`${memberName}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`, - ), - EnumNumberMemberNotInitialized: _<{| - enumName: string, - memberName: string, - |}>( - ({ enumName, memberName }) => - `Number enum members need to be initialized, e.g. \`${memberName} = 1\` in enum \`${enumName}\`.`, - ), - EnumStringMemberInconsistentlyInitailized: _<{| enumName: string |}>( - ({ enumName }) => - `String enum members need to consistently either all use initializers, or use no initializers, in enum \`${enumName}\`.`, - ), - GetterMayNotHaveThisParam: _("A getter cannot have a `this` parameter."), - ImportTypeShorthandOnlyInPureImport: _( - "The `type` and `typeof` keywords on named imports can only be used on regular `import` statements. It cannot be used with `import type` or `import typeof` statements.", - ), - InexactInsideExact: _( - "Explicit inexact syntax cannot appear inside an explicit exact object type.", - ), - InexactInsideNonObject: _( - "Explicit inexact syntax cannot appear in class or interface definitions.", - ), - InexactVariance: _("Explicit inexact syntax cannot have variance."), - InvalidNonTypeImportInDeclareModule: _( - "Imports within a `declare module` body must always be `import type` or `import typeof`.", - ), - MissingTypeParamDefault: _( - "Type parameter declaration needs a default, since a preceding type parameter declaration has a default.", - ), - NestedDeclareModule: _( - "`declare module` cannot be used inside another `declare module`.", - ), - NestedFlowComment: _( - "Cannot have a flow comment inside another flow comment.", - ), - PatternIsOptional: _( - "A binding pattern parameter cannot be optional in an implementation signature.", - // For consistency in TypeScript and Flow error codes - !process.env.BABEL_8_BREAKING - ? { reasonCode: "OptionalBindingPattern" } - : {}, - ), - SetterMayNotHaveThisParam: _("A setter cannot have a `this` parameter."), - SpreadVariance: _("Spread properties cannot have variance."), - ThisParamAnnotationRequired: _( - "A type annotation is required for the `this` parameter.", - ), - ThisParamBannedInConstructor: _( - "Constructors cannot have a `this` parameter; constructors don't bind `this` like other functions.", - ), - ThisParamMayNotBeOptional: _("The `this` parameter cannot be optional."), - ThisParamMustBeFirst: _( - "The `this` parameter must be the first function parameter.", - ), - ThisParamNoDefault: _("The `this` parameter may not have a default value."), - TypeBeforeInitializer: _( - "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`.", - ), - TypeCastInPattern: _( - "The type cast expression is expected to be wrapped with parenthesis.", - ), - UnexpectedExplicitInexactInObject: _( - "Explicit inexact syntax must appear at the end of an inexact object.", - ), - UnexpectedReservedType: _<{| reservedType: string |}>( - ({ reservedType }) => `Unexpected reserved type ${reservedType}.`, - ), - UnexpectedReservedUnderscore: _( - "`_` is only allowed as a type argument to call or new.", - ), - UnexpectedSpaceBetweenModuloChecks: _( - "Spaces between `%` and `checks` are not allowed here.", - ), - UnexpectedSpreadType: _( - "Spread operator cannot appear in class or interface definitions.", - ), - UnexpectedSubtractionOperand: _( - 'Unexpected token, expected "number" or "bigint".', - ), - UnexpectedTokenAfterTypeParameter: _( - "Expected an arrow function after this type parameter declaration.", - ), - UnexpectedTypeParameterBeforeAsyncArrowFunction: _( - "Type parameters must come after the async keyword, e.g. instead of ` async () => {}`, use `async () => {}`.", - ), - UnsupportedDeclareExportKind: _<{| - unsupportedExportKind: string, - suggestion: string, - |}>( - ({ unsupportedExportKind, suggestion }) => - `\`declare export ${unsupportedExportKind}\` is not supported. Use \`${suggestion}\` instead.`, - ), - UnsupportedStatementInDeclareModule: _( - "Only declares and type imports are allowed inside declare module.", - ), - UnterminatedFlowComment: _("Unterminated flow-comment."), -})); +const FlowErrors = ParseErrorEnum`flow`( + (_: typeof toParseErrorCredentials) => ({ + AmbiguousConditionalArrow: _( + "Ambiguous expression: wrap the arrow functions in parentheses to disambiguate.", + ), + AmbiguousDeclareModuleKind: _( + "Found both `declare module.exports` and `declare export` in the same module. Modules can only have 1 since they are either an ES module or they are a CommonJS module.", + ), + // TODO: When we get proper string enums in typescript make this ReservedType. + // Not really worth it to do the whole $Values dance with reservedTypes set. + AssignReservedType: _<{ + reservedType: string; + }>(({ reservedType }) => `Cannot overwrite reserved type ${reservedType}.`), + DeclareClassElement: _( + "The `declare` modifier can only appear on class fields.", + ), + DeclareClassFieldInitializer: _( + "Initializers are not allowed in fields with the `declare` modifier.", + ), + DuplicateDeclareModuleExports: _( + "Duplicate `declare module.exports` statement.", + ), + EnumBooleanMemberNotInitialized: _<{ + memberName: string; + enumName: string; + }>( + ({ memberName, enumName }) => + `Boolean enum members need to be initialized. Use either \`${memberName} = true,\` or \`${memberName} = false,\` in enum \`${enumName}\`.`, + ), + EnumDuplicateMemberName: _<{ + memberName: string; + enumName: string; + }>( + ({ memberName, enumName }) => + `Enum member names need to be unique, but the name \`${memberName}\` has already been used before in enum \`${enumName}\`.`, + ), + EnumInconsistentMemberValues: _<{ + enumName: string; + }>( + ({ enumName }) => + `Enum \`${enumName}\` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers.`, + ), + EnumInvalidExplicitType: _<{ + invalidEnumType: string; + enumName: string; + }>( + ({ invalidEnumType, enumName }) => + `Enum type \`${invalidEnumType}\` is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.`, + ), + EnumInvalidExplicitTypeUnknownSupplied: _<{ + enumName: string; + }>( + ({ enumName }) => + `Supplied enum type is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.`, + ), + + // TODO: When moving to typescript, we should either have each of the + // following errors only accept the specific strings they want: + // + // ...PrimaryType: explicitType: "string" | "number" | "boolean" + // ...SymbolType: explicitType: "symbol" + // ...UnknownType: explicitType: null + // + // Or, alternatively, merge these three errors together into one + // `EnumInvalidMemberInitializer` error that can accept `EnumExplicitType` + // without alteration, and then just have its message change based on the + // explicitType. + EnumInvalidMemberInitializerPrimaryType: _<{ + enumName: string; + memberName: string; + explicitType: EnumExplicitType; + }>( + ({ enumName, memberName, explicitType }) => + `Enum \`${enumName}\` has type \`${explicitType}\`, so the initializer of \`${memberName}\` needs to be a ${explicitType} literal.`, + ), + EnumInvalidMemberInitializerSymbolType: _<{ + enumName: string; + memberName: string; + explicitType: EnumExplicitType; + }>( + ({ enumName, memberName }) => + `Symbol enum members cannot be initialized. Use \`${memberName},\` in enum \`${enumName}\`.`, + ), + EnumInvalidMemberInitializerUnknownType: _<{ + enumName: string; + memberName: string; + explicitType: EnumExplicitType; + }>( + ({ enumName, memberName }) => + `The enum member initializer for \`${memberName}\` needs to be a literal (either a boolean, number, or string) in enum \`${enumName}\`.`, + ), + EnumInvalidMemberName: _<{ + enumName: string; + memberName: string; + suggestion: string; + }>( + ({ enumName, memberName, suggestion }) => + `Enum member names cannot start with lowercase 'a' through 'z'. Instead of using \`${memberName}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`, + ), + EnumNumberMemberNotInitialized: _<{ + enumName: string; + memberName: string; + }>( + ({ enumName, memberName }) => + `Number enum members need to be initialized, e.g. \`${memberName} = 1\` in enum \`${enumName}\`.`, + ), + EnumStringMemberInconsistentlyInitailized: _<{ + enumName: string; + }>( + ({ enumName }) => + `String enum members need to consistently either all use initializers, or use no initializers, in enum \`${enumName}\`.`, + ), + GetterMayNotHaveThisParam: _("A getter cannot have a `this` parameter."), + ImportTypeShorthandOnlyInPureImport: _( + "The `type` and `typeof` keywords on named imports can only be used on regular `import` statements. It cannot be used with `import type` or `import typeof` statements.", + ), + InexactInsideExact: _( + "Explicit inexact syntax cannot appear inside an explicit exact object type.", + ), + InexactInsideNonObject: _( + "Explicit inexact syntax cannot appear in class or interface definitions.", + ), + InexactVariance: _("Explicit inexact syntax cannot have variance."), + InvalidNonTypeImportInDeclareModule: _( + "Imports within a `declare module` body must always be `import type` or `import typeof`.", + ), + MissingTypeParamDefault: _( + "Type parameter declaration needs a default, since a preceding type parameter declaration has a default.", + ), + NestedDeclareModule: _( + "`declare module` cannot be used inside another `declare module`.", + ), + NestedFlowComment: _( + "Cannot have a flow comment inside another flow comment.", + ), + PatternIsOptional: _( + "A binding pattern parameter cannot be optional in an implementation signature.", + // For consistency in TypeScript and Flow error codes + !process.env.BABEL_8_BREAKING + ? { reasonCode: "OptionalBindingPattern" } + : {}, + ), + SetterMayNotHaveThisParam: _("A setter cannot have a `this` parameter."), + SpreadVariance: _("Spread properties cannot have variance."), + ThisParamAnnotationRequired: _( + "A type annotation is required for the `this` parameter.", + ), + ThisParamBannedInConstructor: _( + "Constructors cannot have a `this` parameter; constructors don't bind `this` like other functions.", + ), + ThisParamMayNotBeOptional: _("The `this` parameter cannot be optional."), + ThisParamMustBeFirst: _( + "The `this` parameter must be the first function parameter.", + ), + ThisParamNoDefault: _("The `this` parameter may not have a default value."), + TypeBeforeInitializer: _( + "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`.", + ), + TypeCastInPattern: _( + "The type cast expression is expected to be wrapped with parenthesis.", + ), + UnexpectedExplicitInexactInObject: _( + "Explicit inexact syntax must appear at the end of an inexact object.", + ), + UnexpectedReservedType: _<{ + reservedType: string; + }>(({ reservedType }) => `Unexpected reserved type ${reservedType}.`), + UnexpectedReservedUnderscore: _( + "`_` is only allowed as a type argument to call or new.", + ), + UnexpectedSpaceBetweenModuloChecks: _( + "Spaces between `%` and `checks` are not allowed here.", + ), + UnexpectedSpreadType: _( + "Spread operator cannot appear in class or interface definitions.", + ), + UnexpectedSubtractionOperand: _( + 'Unexpected token, expected "number" or "bigint".', + ), + UnexpectedTokenAfterTypeParameter: _( + "Expected an arrow function after this type parameter declaration.", + ), + UnexpectedTypeParameterBeforeAsyncArrowFunction: _( + "Type parameters must come after the async keyword, e.g. instead of ` async () => {}`, use `async () => {}`.", + ), + UnsupportedDeclareExportKind: _<{ + unsupportedExportKind: string; + suggestion: string; + }>( + ({ unsupportedExportKind, suggestion }) => + `\`declare export ${unsupportedExportKind}\` is not supported. Use \`${suggestion}\` instead.`, + ), + UnsupportedStatementInDeclareModule: _( + "Only declares and type imports are allowed inside declare module.", + ), + UnterminatedFlowComment: _("Unterminated flow-comment."), + }), +); /* eslint-disable sort-keys */ function isEsModuleType(bodyElement: N.Node): boolean { @@ -268,10 +284,10 @@ const exportSuggestions = { // Like Array#filter, but returns a tuple [ acceptedElements, discardedElements ] function partition( list: T[], - test: (T, number, T[]) => ?boolean, + test: (c: T, b: number, a: T[]) => boolean | undefined | null, ): [T[], T[]] { - const list1 = []; - const list2 = []; + const list1: T[] = []; + const list2: T[] = []; for (let i = 0; i < list.length; i++) { (test(list[i], i, list) ? list1 : list2).push(list[i]); } @@ -282,26 +298,52 @@ const FLOW_PRAGMA_REGEX = /\*?\s*@((?:no)?flow)\b/; // Flow enums types type EnumExplicitType = null | "boolean" | "number" | "string" | "symbol"; -type EnumContext = {| - enumName: string, - explicitType: EnumExplicitType, - memberName: string, -|}; -type EnumMemberInit = - | {| type: "number", loc: Position, value: N.Node |} - | {| type: "string", loc: Position, value: N.Node |} - | {| type: "boolean", loc: Position, value: N.Node |} - | {| type: "invalid", loc: Position |} - | {| type: "none", loc: Position |}; -export default (superClass: Class): Class => +type EnumContext = { + enumName: string; + explicitType: EnumExplicitType; + memberName: string; +}; + +type EnumMemberInit = + | { + type: "number"; + loc: Position; + value: N.Node; + } + | { + type: "string"; + loc: Position; + value: N.Node; + } + | { + type: "boolean"; + loc: Position; + value: N.Node; + } + | { + type: "invalid"; + loc: Position; + } + | { + type: "none"; + loc: Position; + }; + +export default (superClass: { + new (...args: any): Parser; +}): { + new (...args: any): Parser; +} => class extends superClass { // The value of the @flow/@noflow pragma. Initially undefined, transitions // to "@flow" or "@noflow" if we see a pragma. Transitions to null if we are // past the initial comment. flowPragma: void | null | "flow" | "noflow" = undefined; - getScopeHandler(): Class { + getScopeHandler(): { + new (...args: any): FlowScopeHandler; + } { return FlowScopeHandler; } @@ -365,7 +407,7 @@ export default (superClass: Class): Class => }); } if (this.eat(tt.parenL)) { - node.value = this.parseExpression(); + node.value = super.parseExpression(); this.expect(tt.parenR); return this.finishNode(node, "DeclaredPredicate"); } else { @@ -373,7 +415,10 @@ export default (superClass: Class): Class => } } - flowParseTypeAndPredicateInitialiser(): [?N.FlowType, ?N.FlowPredicate] { + flowParseTypeAndPredicateInitialiser(): [ + N.FlowType | undefined | null, + N.FlowPredicate | undefined | null, + ] { const oldInType = this.state.inType; this.state.inType = true; this.expect(tt.colon); @@ -392,21 +437,23 @@ export default (superClass: Class): Class => return [type, predicate]; } - flowParseDeclareClass(node: N.FlowDeclareClass): N.FlowDeclareClass { + flowParseDeclareClass( + node: Undone, + ): N.FlowDeclareClass { this.next(); this.flowParseInterfaceish(node, /*isClass*/ true); return this.finishNode(node, "DeclareClass"); } flowParseDeclareFunction( - node: N.FlowDeclareFunction, + node: Undone, ): N.FlowDeclareFunction { this.next(); const id = (node.id = this.parseIdentifier()); const typeNode = this.startNode(); - const typeContainer = this.startNode(); + const typeContainer = this.startNode(); if (this.match(tt.lt)) { typeNode.typeParameters = this.flowParseTypeParameterDeclaration(); @@ -421,12 +468,8 @@ export default (superClass: Class): Class => typeNode.this = tmp._this; this.expect(tt.parenR); - [ - // $FlowFixMe (destructuring not supported yet) - typeNode.returnType, - // $FlowFixMe (destructuring not supported yet) - node.predicate, - ] = this.flowParseTypeAndPredicateInitialiser(); + [typeNode.returnType, node.predicate] = + this.flowParseTypeAndPredicateInitialiser(); typeContainer.typeAnnotation = this.finishNode( typeNode, @@ -448,7 +491,7 @@ export default (superClass: Class): Class => } flowParseDeclare( - node: N.FlowDeclare, + node: Undone, insideModule?: boolean, ): N.FlowDeclare { if (this.match(tt._class)) { @@ -482,7 +525,7 @@ export default (superClass: Class): Class => } flowParseDeclareVariable( - node: N.FlowDeclareVariable, + node: Undone, ): N.FlowDeclareVariable { this.next(); node.id = this.flowParseTypeAnnotatableIdentifier( @@ -493,20 +536,23 @@ export default (superClass: Class): Class => return this.finishNode(node, "DeclareVariable"); } - flowParseDeclareModule(node: N.FlowDeclareModule): N.FlowDeclareModule { + flowParseDeclareModule( + node: Undone, + ): N.FlowDeclareModule { this.scope.enter(SCOPE_OTHER); if (this.match(tt.string)) { - node.id = this.parseExprAtom(); + node.id = super.parseExprAtom(); } else { node.id = this.parseIdentifier(); } const bodyNode = (node.body = this.startNode()); + // @ts-expect-error refine typings const body = (bodyNode.body = []); this.expect(tt.braceL); while (!this.match(tt.braceR)) { - let bodyNode = this.startNode(); + let bodyNode = this.startNode(); if (this.match(tt._import)) { this.next(); @@ -515,13 +561,13 @@ export default (superClass: Class): Class => at: this.state.lastTokStartLoc, }); } - this.parseImport(bodyNode); + super.parseImport(bodyNode); } else { this.expectContextual( tt._declare, FlowErrors.UnsupportedStatementInDeclareModule, ); - + // @ts-expect-error refine typings bodyNode = this.flowParseDeclare(bodyNode, true); } @@ -534,7 +580,7 @@ export default (superClass: Class): Class => this.finishNode(bodyNode, "BlockStatement"); - let kind = null; + let kind: "CommonJS" | "ES" | null = null; let hasModuleExport = false; body.forEach(bodyElement => { if (isEsModuleType(bodyElement)) { @@ -565,8 +611,8 @@ export default (superClass: Class): Class => } flowParseDeclareExportDeclaration( - node: N.FlowDeclareExportDeclaration, - insideModule: ?boolean, + node: Undone, + insideModule?: boolean | null, ): N.FlowDeclareExportDeclaration { this.expect(tt._export); @@ -590,7 +636,11 @@ export default (superClass: Class): Class => ((this.isContextual(tt._type) || this.isContextual(tt._interface)) && !insideModule) ) { - const label = this.state.value; + const label = this.state.value as + | "const" + | "let" + | "type" + | "interface"; throw this.raise(FlowErrors.UnsupportedDeclareExportKind, { at: this.state.startLoc, unsupportedExportKind: label, @@ -615,20 +665,18 @@ export default (superClass: Class): Class => this.isContextual(tt._type) || // declare export type ... this.isContextual(tt._opaque) // declare export opaque type ... ) { - node = this.parseExport(node); + node = this.parseExport( + node as Undone, + ); if (node.type === "ExportNamedDeclaration") { - // flow does not support the ExportNamedDeclaration - // $FlowIgnore node.type = "ExportDeclaration"; - // $FlowFixMe node.default = false; delete node.exportKind; } - // $FlowIgnore node.type = "Declare" + node.type; - return node; + return node as N.Node; } } @@ -636,7 +684,7 @@ export default (superClass: Class): Class => } flowParseDeclareModuleExports( - node: N.FlowDeclareModuleExports, + node: Undone, ): N.FlowDeclareModuleExports { this.next(); this.expectContextual(tt._exports); @@ -647,27 +695,27 @@ export default (superClass: Class): Class => } flowParseDeclareTypeAlias( - node: N.FlowDeclareTypeAlias, + node: Undone, ): N.FlowDeclareTypeAlias { this.next(); - this.flowParseTypeAlias(node); + const finished = this.flowParseTypeAlias(node); // Don't do finishNode as we don't want to process comments twice - node.type = "DeclareTypeAlias"; - return node; + finished.type = "DeclareTypeAlias"; + return finished; } flowParseDeclareOpaqueType( - node: N.FlowDeclareOpaqueType, + node: Undone, ): N.FlowDeclareOpaqueType { this.next(); - this.flowParseOpaqueType(node, true); + const finished = this.flowParseOpaqueType(node, true); // Don't do finishNode as we don't want to process comments twice - node.type = "DeclareOpaqueType"; - return node; + finished.type = "DeclareOpaqueType"; + return finished; } flowParseDeclareInterface( - node: N.FlowDeclareInterface, + node: Undone, ): N.FlowDeclareInterface { this.next(); this.flowParseInterfaceish(node); @@ -677,8 +725,8 @@ export default (superClass: Class): Class => // Interfaces flowParseInterfaceish( - node: N.FlowDeclare, - isClass?: boolean = false, + node: Undone, + isClass: boolean = false, ): void { node.id = this.flowParseRestrictedIdentifier( /* liberal */ !isClass, @@ -743,7 +791,7 @@ export default (superClass: Class): Class => return this.finishNode(node, "InterfaceExtends"); } - flowParseInterface(node: N.FlowInterface): N.FlowInterface { + flowParseInterface(node: Undone): N.FlowInterface { this.flowParseInterfaceish(node); return this.finishNode(node, "InterfaceDeclaration"); } @@ -784,7 +832,7 @@ export default (superClass: Class): Class => // Type aliases - flowParseTypeAlias(node: N.FlowTypeAlias): N.FlowTypeAlias { + flowParseTypeAlias(node: Undone): N.FlowTypeAlias { node.id = this.flowParseRestrictedIdentifier( /* liberal */ false, /* declaration */ true, @@ -804,7 +852,7 @@ export default (superClass: Class): Class => } flowParseOpaqueType( - node: N.FlowOpaqueType, + node: Undone, declare: boolean, ): N.FlowOpaqueType { this.expectContextual(tt._type); @@ -837,20 +885,23 @@ export default (superClass: Class): Class => // Type annotations - flowParseTypeParameter(requireDefault?: boolean = false): N.TypeParameter { + flowParseTypeParameter(requireDefault: boolean = false): N.TypeParameter { const nodeStartLoc = this.state.startLoc; - const node = this.startNode(); + const node = this.startNode(); const variance = this.flowParseVariance(); const ident = this.flowParseTypeAnnotatableIdentifier(); node.name = ident.name; + // @ts-expect-error migrate to Babel types node.variance = variance; + // @ts-expect-error migrate to Babel types node.bound = ident.typeAnnotation; if (this.match(tt.eq)) { this.eat(tt.eq); + // @ts-expect-error migrate to Babel types node.default = this.flowParseType(); } else { if (requireDefault) { @@ -863,7 +914,7 @@ export default (superClass: Class): Class => flowParseTypeParameterDeclaration(): N.TypeParameterDeclaration { const oldInType = this.state.inType; - const node = this.startNode(); + const node = this.startNode(); node.params = []; this.state.inType = true; @@ -898,7 +949,7 @@ export default (superClass: Class): Class => } flowParseTypeParameterInstantiation(): N.TypeParameterInstantiation { - const node = this.startNode(); + const node = this.startNode(); const oldInType = this.state.inType; node.params = []; @@ -922,7 +973,7 @@ export default (superClass: Class): Class => } flowParseTypeParameterInstantiationCallOrNew(): N.TypeParameterInstantiation { - const node = this.startNode(); + const node = this.startNode(); const oldInType = this.state.inType; node.params = []; @@ -943,7 +994,7 @@ export default (superClass: Class): Class => } flowParseInterfaceType(): N.FlowInterfaceType { - const node = this.startNode(); + const node = this.startNode(); this.expectContextual(tt._interface); node.extends = []; @@ -966,14 +1017,14 @@ export default (superClass: Class): Class => flowParseObjectPropertyKey(): N.Expression { return this.match(tt.num) || this.match(tt.string) - ? this.parseExprAtom() + ? super.parseExprAtom() : this.parseIdentifier(true); } flowParseObjectTypeIndexer( - node: N.FlowObjectTypeIndexer, + node: Undone, isStatic: boolean, - variance: ?N.FlowVariance, + variance?: N.FlowVariance | null, ): N.FlowObjectTypeIndexer { node.static = isStatic; @@ -993,7 +1044,7 @@ export default (superClass: Class): Class => } flowParseObjectTypeInternalSlot( - node: N.FlowObjectTypeInternalSlot, + node: Undone, isStatic: boolean, ): N.FlowObjectTypeInternalSlot { node.static = isStatic; @@ -1018,7 +1069,7 @@ export default (superClass: Class): Class => } flowParseObjectTypeMethodish( - node: N.FlowFunctionTypeAnnotation, + node: Undone, ): N.FlowFunctionTypeAnnotation { node.params = []; node.rest = null; @@ -1055,7 +1106,7 @@ export default (superClass: Class): Class => } flowParseObjectTypeCallProperty( - node: N.FlowObjectTypeCallProperty, + node: Undone, isStatic: boolean, ): N.FlowObjectTypeCallProperty { const valueNode = this.startNode(); @@ -1071,11 +1122,11 @@ export default (superClass: Class): Class => allowProto, allowInexact, }: { - allowStatic: boolean, - allowExact: boolean, - allowSpread: boolean, - allowProto: boolean, - allowInexact: boolean, + allowStatic: boolean; + allowExact: boolean; + allowSpread: boolean; + allowProto: boolean; + allowInexact: boolean; }): N.FlowObjectTypeAnnotation { const oldInType = this.state.inType; this.state.inType = true; @@ -1104,8 +1155,8 @@ export default (superClass: Class): Class => while (!this.match(endDelim)) { let isStatic = false; - let protoStartLoc: ?Position = null; - let inexactStartLoc: ?Position = null; + let protoStartLoc: Position | undefined | null = null; + let inexactStartLoc: Position | undefined | null = null; const node = this.startNode(); if (allowProto && this.isContextual(tt._proto)) { @@ -1217,14 +1268,14 @@ export default (superClass: Class): Class => } flowParseObjectTypeProperty( - node: N.FlowObjectTypeProperty | N.FlowObjectTypeSpreadProperty, + node: Undone, isStatic: boolean, - protoStartLoc: ?Position, - variance: ?N.FlowVariance, + protoStartLoc: Position | undefined | null, + variance: N.FlowVariance | undefined | null, kind: string, allowSpread: boolean, allowInexact: boolean, - ): (N.FlowObjectTypeProperty | N.FlowObjectTypeSpreadProperty) | null { + ): N.FlowObjectTypeProperty | N.FlowObjectTypeSpreadProperty | null { if (this.eat(tt.ellipsis)) { const isInexactToken = this.match(tt.comma) || @@ -1318,7 +1369,9 @@ export default (superClass: Class): Class => // This is similar to checkGetterSetterParams, but as // @babel/parser uses non estree properties we cannot reuse it here flowCheckGetterSetterParams( - property: N.FlowObjectTypeProperty | N.FlowObjectTypeSpreadProperty, + property: Undone< + N.FlowObjectTypeProperty | N.FlowObjectTypeSpreadProperty + >, ): void { const paramCount = property.kind === "get" ? 0 : 1; const length = @@ -1365,10 +1418,14 @@ export default (superClass: Class): Class => ): N.FlowQualifiedTypeIdentifier { startPos = startPos || this.state.start; startLoc = startLoc || this.state.startLoc; - let node = id || this.flowParseRestrictedIdentifier(true); + let node: N.Identifier | N.FlowQualifiedTypeIdentifier = + id || this.flowParseRestrictedIdentifier(true); while (this.eat(tt.dot)) { - const node2 = this.startNodeAt(startPos, startLoc); + const node2 = this.startNodeAt( + startPos, + startLoc, + ); node2.qualification = node; node2.id = this.flowParseRestrictedIdentifier(true); node = this.finishNode(node2, "QualifiedTypeIdentifier"); @@ -1419,7 +1476,7 @@ export default (superClass: Class): Class => let name = null; let optional = false; let typeAnnotation = null; - const node = this.startNode(); + const node = this.startNode(); const lh = this.lookahead(); const isThis = this.state.type === tt._this; @@ -1455,12 +1512,12 @@ export default (superClass: Class): Class => } flowParseFunctionTypeParams(params: N.FlowFunctionTypeParam[] = []): { - params: N.FlowFunctionTypeParam[], - rest: ?N.FlowFunctionTypeParam, - _this: ?N.FlowFunctionTypeParam, + params: N.FlowFunctionTypeParam[]; + rest: N.FlowFunctionTypeParam | undefined | null; + _this: N.FlowFunctionTypeParam | undefined | null; } { - let rest: ?N.FlowFunctionTypeParam = null; - let _this: ?N.FlowFunctionTypeParam = null; + let rest: N.FlowFunctionTypeParam | undefined | null = null; + let _this: N.FlowFunctionTypeParam | undefined | null = null; if (this.match(tt._this)) { _this = this.flowParseFunctionTypeParam(/* first */ true); // match Flow parser behavior @@ -1484,7 +1541,7 @@ export default (superClass: Class): Class => flowIdentToTypeAnnotation( startPos: number, startLoc: Position, - node: N.FlowTypeAnnotation, + node: Undone, id: N.Identifier, ): N.FlowTypeAnnotation { switch (id.name) { @@ -1634,8 +1691,8 @@ export default (superClass: Class): Class => case tt._false: node.value = this.match(tt._true); this.next(); - return this.finishNode( - node, + return this.finishNode( + node as Undone, "BooleanLiteralTypeAnnotation", ); @@ -1699,7 +1756,7 @@ export default (superClass: Class): Class => if (tokenIsKeyword(this.state.type)) { const label = tokenLabelName(this.state.type); this.next(); - return super.createIdentifier(node, label); + return super.createIdentifier(node as Undone, label); } else if (tokenIsIdentifier(this.state.type)) { if (this.isContextual(tt._interface)) { return this.flowParseInterfaceType(); @@ -1826,7 +1883,7 @@ export default (superClass: Class): Class => } flowParseTypeAnnotation(): N.FlowTypeAnnotation { - const node = this.startNode(); + const node = this.startNode(); node.typeAnnotation = this.flowParseTypeInitialiser(); return this.finishNode(node, "TypeAnnotation"); } @@ -1838,6 +1895,7 @@ export default (superClass: Class): Class => ? this.parseIdentifier() : this.flowParseRestrictedIdentifier(); if (this.match(tt.colon)) { + // @ts-expect-error: refine typings ident.typeAnnotation = this.flowParseTypeAnnotation(); this.resetEndLocation(ident); } @@ -1852,17 +1910,17 @@ export default (superClass: Class): Class => return node.expression; } - flowParseVariance(): ?N.FlowVariance { + flowParseVariance(): N.FlowVariance | undefined | null { let variance = null; if (this.match(tt.plusMin)) { - variance = this.startNode(); + variance = this.startNode(); if (this.state.value === "+") { variance.kind = "plus"; } else { variance.kind = "minus"; } this.next(); - this.finishNode(variance, "Variance"); + return this.finishNode(variance, "Variance"); } return variance; } @@ -1873,8 +1931,8 @@ export default (superClass: Class): Class => parseFunctionBody( node: N.Function, - allowExpressionBody: ?boolean, - isMethod?: boolean = false, + allowExpressionBody?: boolean | null, + isMethod: boolean = false, ): void { if (allowExpressionBody) { return this.forwardNoArrowParamsConversionAt(node, () => @@ -1885,18 +1943,19 @@ export default (superClass: Class): Class => return super.parseFunctionBody(node, false, isMethod); } - parseFunctionBodyAndFinish( - node: N.BodilessFunctionOrMethodBase, - type: string, - isMethod?: boolean = false, - ): void { + parseFunctionBodyAndFinish< + T extends + | N.Function + | N.TSDeclareMethod + | N.TSDeclareFunction + | N.ClassPrivateMethod, + >(node: Undone, type: T["type"], isMethod: boolean = false): T { if (this.match(tt.colon)) { - const typeNode = this.startNode(); + const typeNode = this.startNode(); [ - // $FlowFixMe (destructuring not supported yet) typeNode.typeAnnotation, - // $FlowFixMe (destructuring not supported yet) + // @ts-expect-error predicate may not exist node.predicate, ] = this.flowParseTypeAndPredicateInitialiser(); @@ -1905,16 +1964,16 @@ export default (superClass: Class): Class => : null; } - super.parseFunctionBodyAndFinish(node, type, isMethod); + return super.parseFunctionBodyAndFinish(node, type, isMethod); } // interfaces and enums - parseStatement(context: ?string, topLevel?: boolean): N.Statement { + parseStatement(context?: string | null, topLevel?: boolean): N.Statement { // strict mode handling of `interface` since it's a reserved word if (this.state.strict && this.isContextual(tt._interface)) { const lookahead = this.lookahead(); if (tokenIsKeywordOrIdentifier(lookahead.type)) { - const node = this.startNode(); + const node = this.startNode(); this.next(); return this.flowParseInterface(node); } @@ -1945,14 +2004,18 @@ export default (superClass: Class): Class => this.match(tt._var) || this.match(tt._export) ) { + // @ts-expect-error: refine typings return this.flowParseDeclare(node); } } else if (tokenIsIdentifier(this.state.type)) { if (expr.name === "interface") { + // @ts-expect-error: refine typings return this.flowParseInterface(node); } else if (expr.name === "type") { + // @ts-expect-error: refine typings return this.flowParseTypeAlias(node); } else if (expr.name === "opaque") { + // @ts-expect-error: refine typings return this.flowParseOpaqueType(node, false); } } @@ -1998,7 +2061,7 @@ export default (superClass: Class): Class => expr: N.Expression, startPos: number, startLoc: Position, - refExpressionErrors?: ?ExpressionErrors, + refExpressionErrors?: ExpressionErrors | null, ): N.Expression { if (!this.match(tt.question)) return expr; @@ -2075,8 +2138,8 @@ export default (superClass: Class): Class => } tryParseConditionalConsequent(): { - consequent: N.Expression, - failed: boolean, + consequent: N.Expression; + failed: boolean; } { this.state.noArrowParamsConversionAt.push(this.state.start); @@ -2107,8 +2170,10 @@ export default (superClass: Class): Class => if (node.type === "ArrowFunctionExpression") { if (node.typeParameters || !node.returnType) { // This is an arrow expression without ambiguity, so check its parameters + // @ts-expect-error: refine typings this.finishArrowValidation(node); } else { + // @ts-expect-error: refine typings arrows.push(node); } stack.push(node.body); @@ -2132,7 +2197,7 @@ export default (superClass: Class): Class => this.toAssignableList( // node.params is Expression[] instead of $ReadOnlyArray because it // has not been converted yet. - ((node.params: any): N.Expression[]), + node.params as any as N.Expression[], node.extra?.trailingCommaLoc, /* isLHS */ false, ); @@ -2143,7 +2208,10 @@ export default (superClass: Class): Class => this.scope.exit(); } - forwardNoArrowParamsConversionAt(node: N.Node, parse: () => T): T { + forwardNoArrowParamsConversionAt( + node: Undone, + parse: () => T, + ): T { let result: T; if (this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) { this.state.noArrowParamsConversionAt.push(this.state.start); @@ -2197,7 +2265,9 @@ export default (superClass: Class): Class => super.assertModuleNodeAllowed(node); } - parseExport(node: N.Node): N.AnyExport { + parseExport( + node: Undone, + ): N.AnyExport { const decl = super.parseExport(node); if ( decl.type === "ExportNamedDeclaration" || @@ -2208,7 +2278,9 @@ export default (superClass: Class): Class => return decl; } - parseExportDeclaration(node: N.ExportNamedDeclaration): ?N.Declaration { + parseExportDeclaration( + node: N.ExportNamedDeclaration, + ): N.Declaration | undefined | null { if (this.isContextual(tt._type)) { node.exportKind = "type"; @@ -2220,10 +2292,11 @@ export default (superClass: Class): Class => node.specifiers = this.parseExportSpecifiers( /* isInTypeExport */ true, ); - this.parseExportFrom(node); + super.parseExportFrom(node); return null; } else { // export type Foo = Bar; + // @ts-expect-error: refine typings return this.flowParseTypeAlias(declarationNode); } } else if (this.isContextual(tt._opaque)) { @@ -2232,16 +2305,19 @@ export default (superClass: Class): Class => const declarationNode = this.startNode(); this.next(); // export opaque type Foo = Bar; + // @ts-expect-error: refine typings return this.flowParseOpaqueType(declarationNode, false); } else if (this.isContextual(tt._interface)) { node.exportKind = "type"; const declarationNode = this.startNode(); this.next(); + // @ts-expect-error: refine typings return this.flowParseInterface(declarationNode); } else if (this.shouldParseEnums() && this.isContextual(tt._enum)) { node.exportKind = "value"; const declarationNode = this.startNode(); this.next(); + // @ts-expect-error: refine typings return this.flowParseEnumDeclaration(declarationNode); } else { return super.parseExportDeclaration(node); @@ -2249,7 +2325,7 @@ export default (superClass: Class): Class => } eatExportStar(node: N.Node): boolean { - if (super.eatExportStar(...arguments)) return true; + if (super.eatExportStar(node)) return true; if (this.isContextual(tt._type) && this.lookahead().type === tt.star) { node.exportKind = "type"; @@ -2270,7 +2346,11 @@ export default (superClass: Class): Class => return hasNamespace; } - parseClassId(node: N.Class, isStatement: boolean, optionalId: ?boolean) { + parseClassId( + node: N.Class, + isStatement: boolean, + optionalId?: boolean | null, + ) { super.parseClassId(node, isStatement, optionalId); if (this.match(tt.lt)) { node.typeParameters = this.flowParseTypeParameterDeclaration(); @@ -2284,7 +2364,7 @@ export default (superClass: Class): Class => ): void { const { startLoc } = this.state; if (this.isContextual(tt._declare)) { - if (this.parseClassMemberFromModifier(classBody, member)) { + if (super.parseClassMemberFromModifier(classBody, member)) { // 'declare' is a class element name return; } @@ -2370,13 +2450,13 @@ export default (superClass: Class): Class => ) { node.left = this.typeCastToParameter(node.left); } - super.toAssignable(...arguments); + super.toAssignable(node, isLHS); } // turn type casts that we found in function parameter head into type annotated params toAssignableList( exprList: N.Expression[], - trailingCommaLoc?: ?Position, + trailingCommaLoc: Position | undefined | null, isLHS: boolean, ): void { for (let i = 0; i < exprList.length; i++) { @@ -2391,9 +2471,9 @@ export default (superClass: Class): Class => // this is a list of nodes, from something like a call expression, we need to filter the // type casts that we've found that are illegal in this context toReferencedList( - exprList: $ReadOnlyArray, + exprList: ReadonlyArray, isParenthesizedExpr?: boolean, - ): $ReadOnlyArray { + ): ReadonlyArray { for (let i = 0; i < exprList.length; i++) { const expr = exprList[i]; if ( @@ -2415,7 +2495,7 @@ export default (superClass: Class): Class => close: TokenType, canBePattern: boolean, isTuple: boolean, - refExpressionErrors: ?ExpressionErrors, + refExpressionErrors?: ExpressionErrors | null, ): N.ArrayExpression | N.TupleExpression { const node = super.parseArrayLike( close, @@ -2436,13 +2516,14 @@ export default (superClass: Class): Class => return node; } - isValidLVal(type: string, ...rest) { + isValidLVal(type: string, ...rest: [boolean, BindingTypes]) { return type === "TypeCastExpression" || super.isValidLVal(type, ...rest); } // parse class property type annotations parseClassProperty(node: N.ClassProperty): N.ClassProperty { if (this.match(tt.colon)) { + // @ts-expect-error refine typings node.typeAnnotation = this.flowParseTypeAnnotation(); } return super.parseClassProperty(node); @@ -2452,6 +2533,7 @@ export default (superClass: Class): Class => node: N.ClassPrivateProperty, ): N.ClassPrivateProperty { if (this.match(tt.colon)) { + // @ts-expect-error refine typings node.typeAnnotation = this.flowParseTypeAnnotation(); } return super.parseClassPrivateProperty(node); @@ -2480,10 +2562,10 @@ export default (superClass: Class): Class => isConstructor: boolean, allowsDirectSuper: boolean, ): void { - if ((method: $FlowFixMe).variance) { - this.unexpected((method: $FlowFixMe).variance.loc.start); + if ((method as any).variance) { + this.unexpected((method as any).variance.loc.start); } - delete (method: $FlowFixMe).variance; + delete (method as any).variance; if (this.match(tt.lt)) { method.typeParameters = this.flowParseTypeParameterDeclaration(); } @@ -2504,11 +2586,13 @@ export default (superClass: Class): Class => } // estree support } else if ( - // $FlowFixMe flow does not know about the face that estree can replace ClassMethod with MethodDefinition + // @ts-expect-error flow does not know about the face that estree can replace ClassMethod with MethodDefinition method.type === "MethodDefinition" && isConstructor && + // @ts-expect-error estree method.value.params ) { + // @ts-expect-error estree const params = method.value.params; if (params.length > 0 && this.isThisParam(params[0])) { this.raise(FlowErrors.ThisParamBannedInConstructor, { at: method }); @@ -2522,10 +2606,10 @@ export default (superClass: Class): Class => isGenerator: boolean, isAsync: boolean, ): void { - if ((method: $FlowFixMe).variance) { - this.unexpected((method: $FlowFixMe).variance.loc.start); + if ((method as any).variance) { + this.unexpected((method as any).variance.loc.start); } - delete (method: $FlowFixMe).variance; + delete (method as any).variance; if (this.match(tt.lt)) { method.typeParameters = this.flowParseTypeParameterDeclaration(); } @@ -2576,19 +2660,19 @@ export default (superClass: Class): Class => // parse type parameters for object method shorthand parseObjPropValue( - prop: N.ObjectMember, - startPos: ?number, - startLoc: ?Position, + prop: Undone, + startPos: number | undefined | null, + startLoc: Position | undefined | null, isGenerator: boolean, isAsync: boolean, isPattern: boolean, isAccessor: boolean, - refExpressionErrors: ?ExpressionErrors, - ): void { - if ((prop: $FlowFixMe).variance) { - this.unexpected((prop: $FlowFixMe).variance.loc.start); + refExpressionErrors?: ExpressionErrors | null, + ): N.ObjectMethod | N.ObjectProperty { + if ((prop as any).variance) { + this.unexpected((prop as any).variance.loc.start); } - delete (prop: $FlowFixMe).variance; + delete (prop as any).variance; let typeParameters; @@ -2598,7 +2682,7 @@ export default (superClass: Class): Class => if (!this.match(tt.parenL)) this.unexpected(); } - super.parseObjPropValue( + const result = super.parseObjPropValue( prop, startPos, startLoc, @@ -2611,8 +2695,9 @@ export default (superClass: Class): Class => // add typeParameters if we found them if (typeParameters) { - (prop.value || prop).typeParameters = typeParameters; + (result.value || result).typeParameters = typeParameters; } + return result; } parseAssignableListItemTypes(param: N.Pattern): N.Pattern { @@ -2624,9 +2709,10 @@ export default (superClass: Class): Class => this.raise(FlowErrors.ThisParamMayNotBeOptional, { at: param }); } - ((param: any): N.Identifier).optional = true; + (param as any as N.Identifier).optional = true; } if (this.match(tt.colon)) { + // @ts-expect-error: refine typings param.typeAnnotation = this.flowParseTypeAnnotation(); } else if (this.isThisParam(param)) { this.raise(FlowErrors.ThisParamAnnotationRequired, { at: param }); @@ -2641,9 +2727,9 @@ export default (superClass: Class): Class => } parseMaybeDefault( - startPos?: ?number, - startLoc?: ?Position, - left?: ?N.Pattern, + startPos?: number | null, + startLoc?: Position | null, + left?: N.Pattern | null, ): N.Pattern { const node = super.parseMaybeDefault(startPos, startLoc, left); @@ -2668,11 +2754,12 @@ export default (superClass: Class): Class => return isMaybeDefaultImport(this.state.type); } - parseImportSpecifierLocal( - node: N.ImportDeclaration, - specifier: N.Node, - type: string, - ): void { + parseImportSpecifierLocal< + T extends + | N.ImportSpecifier + | N.ImportDefaultSpecifier + | N.ImportNamespaceSpecifier, + >(node: N.ImportDeclaration, specifier: Undone, type: T["type"]): void { specifier.local = hasTypeImportKind(node) ? this.flowParseRestrictedIdentifier( /* liberal */ true, @@ -2689,9 +2776,9 @@ export default (superClass: Class): Class => let kind = null; if (this.match(tt._typeof)) { - kind = "typeof"; + kind = "typeof" as const; } else if (this.isContextual(tt._type)) { - kind = "type"; + kind = "type" as const; } if (kind) { const lh = this.lookahead(); @@ -2721,7 +2808,7 @@ export default (superClass: Class): Class => specifier: any, importedIsString: boolean, isInTypeOnlyImport: boolean, - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars isMaybeTypeOnly: boolean, ): N.ImportSpecifier { const firstIdent = specifier.imported; @@ -2820,8 +2907,11 @@ export default (superClass: Class): Class => } // parse function type parameters - function foo() {} - parseFunctionParams(node: N.Function, allowModifiers?: boolean): void { - // $FlowFixMe + parseFunctionParams( + node: Undone, + allowModifiers?: boolean, + ): void { + // @ts-expect-error const kind = node.kind; if (kind !== "get" && kind !== "set" && this.match(tt.lt)) { node.typeParameters = this.flowParseTypeParameterDeclaration(); @@ -2836,6 +2926,7 @@ export default (superClass: Class): Class => ): void { super.parseVarId(decl, kind); if (this.match(tt.colon)) { + // @ts-expect-error: refine typings decl.id.typeAnnotation = this.flowParseTypeAnnotation(); this.resetEndLocation(decl.id); // set end position to end of type } @@ -2849,6 +2940,7 @@ export default (superClass: Class): Class => if (this.match(tt.colon)) { const oldNoAnonFunctionType = this.state.noAnonFunctionType; this.state.noAnonFunctionType = true; + // @ts-expect-error refine typings node.returnType = this.flowParseTypeAnnotation(); this.state.noAnonFunctionType = oldNoAnonFunctionType; } @@ -2872,7 +2964,7 @@ export default (superClass: Class): Class => // there // 3. This is neither. Just call the super method parseMaybeAssign( - refExpressionErrors?: ?ExpressionErrors, + refExpressionErrors?: ExpressionErrors | null, afterLeftParse?: Function, ): N.Expression { let state = null; @@ -2907,7 +2999,7 @@ export default (superClass: Class): Class => if (jsx?.error || this.match(tt.lt)) { state = state || this.state.clone(); - let typeParameters; + let typeParameters: N.TypeParameterDeclaration; const arrow = this.tryParse(abort => { typeParameters = this.flowParseTypeParameterDeclaration(); @@ -2943,18 +3035,21 @@ export default (superClass: Class): Class => return arrowExpression; }, state); - let arrowExpression: ?( + let arrowExpression: | N.ArrowFunctionExpression | N.TypeCastExpression - ) = null; + | undefined + | null = null; if ( arrow.node && + // @ts-expect-error: refine tryParse typings this.maybeUnwrapTypeCastExpression(arrow.node).type === "ArrowFunctionExpression" ) { if (!arrow.error && !arrow.aborted) { // async () => {} + // @ts-expect-error: refine tryParse typings if (arrow.node.async) { /*:: invariant(typeParameters) */ this.raise( @@ -2962,10 +3057,11 @@ export default (superClass: Class): Class => { at: typeParameters }, ); } - + // @ts-expect-error: refine tryParse typings return arrow.node; } + // @ts-expect-error: refine typings arrowExpression = arrow.node; } @@ -3000,18 +3096,19 @@ export default (superClass: Class): Class => } // handle return types for arrow functions - parseArrow(node: N.ArrowFunctionExpression): ?N.ArrowFunctionExpression { + parseArrow( + node: Undone, + ): Undone | undefined | null { if (this.match(tt.colon)) { const result = this.tryParse(() => { const oldNoAnonFunctionType = this.state.noAnonFunctionType; this.state.noAnonFunctionType = true; - const typeNode = this.startNode(); + const typeNode = this.startNode(); [ - // $FlowFixMe (destructuring not supported yet) typeNode.typeAnnotation, - // $FlowFixMe (destructuring not supported yet) + // @ts-expect-error (destructuring not supported yet) node.predicate, ] = this.flowParseTypeAndPredicateInitialiser(); @@ -3043,7 +3140,7 @@ export default (superClass: Class): Class => setArrowFunctionParameters( node: N.ArrowFunctionExpression, - params: N.Expression[], + params: N.Pattern[], ): void { if (this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) { node.params = params; @@ -3055,7 +3152,7 @@ export default (superClass: Class): Class => checkParams( node: N.Function, allowDuplicates: boolean, - isArrowFunction: ?boolean, + isArrowFunction?: boolean | null, ): void { if ( isArrowFunction && @@ -3071,7 +3168,7 @@ export default (superClass: Class): Class => } } - return super.checkParams(...arguments); + return super.checkParams(node, allowDuplicates, isArrowFunction); } parseParenAndDistinguishExpression(canBeArrow: boolean): N.Expression { @@ -3084,7 +3181,7 @@ export default (superClass: Class): Class => base: N.Expression, startPos: number, startLoc: Position, - noCalls?: ?boolean, + noCalls?: boolean | null, ): N.Expression { if ( base.type === "Identifier" && @@ -3095,7 +3192,7 @@ export default (superClass: Class): Class => const node = this.startNodeAt(startPos, startLoc); node.callee = base; - node.arguments = this.parseCallExpressionArguments(tt.parenR, false); + node.arguments = super.parseCallExpressionArguments(tt.parenR, false); base = this.finishNode(node, "CallExpression"); } else if ( base.type === "Identifier" && @@ -3111,6 +3208,7 @@ export default (superClass: Class): Class => ); /*:: invariant(arrow.node != null) */ + // @ts-expect-error: refine tryParse typings if (!arrow.error && !arrow.aborted) return arrow.node; const result = this.tryParse( @@ -3122,6 +3220,7 @@ export default (superClass: Class): Class => if (arrow.node) { this.state = arrow.failState; + // @ts-expect-error: refine tryParse typings return arrow.node; } @@ -3140,7 +3239,7 @@ export default (superClass: Class): Class => base: N.Expression, startPos: number, startLoc: Position, - noCalls: ?boolean, + noCalls: boolean | undefined | null, subscriptState: N.ParseSubscriptState, ): N.Expression { if (this.match(tt.questionDot) && this.isLookaheadToken_lt()) { @@ -3150,27 +3249,30 @@ export default (superClass: Class): Class => return base; } this.next(); - const node: N.OptionalCallExpression = this.startNodeAt( + const node = this.startNodeAt( startPos, startLoc, ); node.callee = base; node.typeArguments = this.flowParseTypeParameterInstantiation(); this.expect(tt.parenL); - // $FlowFixMe node.arguments = this.parseCallExpressionArguments(tt.parenR, false); node.optional = true; return this.finishCallExpression(node, /* optional */ true); } else if (!noCalls && this.shouldParseTypes() && this.match(tt.lt)) { - const node = this.startNodeAt(startPos, startLoc); + const node = this.startNodeAt< + N.OptionalCallExpression | N.CallExpression + >(startPos, startLoc); node.callee = base; const result = this.tryParse(() => { node.typeArguments = this.flowParseTypeParameterInstantiationCallOrNew(); this.expect(tt.parenL); - node.arguments = this.parseCallExpressionArguments(tt.parenR, false); - if (subscriptState.optionalChainMember) node.optional = false; + node.arguments = super.parseCallExpressionArguments(tt.parenR, false); + if (subscriptState.optionalChainMember) { + (node as Undone).optional = false; + } return this.finishCallExpression( node, subscriptState.optionalChainMember, @@ -3207,11 +3309,14 @@ export default (superClass: Class): Class => parseAsyncArrowWithTypeParameters( startPos: number, startLoc: Position, - ): ?N.ArrowFunctionExpression { - const node = this.startNodeAt(startPos, startLoc); + ): N.ArrowFunctionExpression | undefined | null { + const node = this.startNodeAt( + startPos, + startLoc, + ); this.parseFunctionParams(node); if (!this.parseArrow(node)) return; - return this.parseArrowExpression( + return super.parseArrowExpression( node, /* params */ undefined, /* isAsync */ true, @@ -3266,8 +3371,11 @@ export default (superClass: Class): Class => }); } this.hasFlowCommentCompletion(); - this.state.pos += this.skipFlowComment(); - this.state.hasFlowComment = true; + const commentSkip = this.skipFlowComment(); + if (commentSkip) { + this.state.pos += commentSkip; + this.state.hasFlowComment = true; + } return; } @@ -3285,11 +3393,12 @@ export default (superClass: Class): Class => return super.skipBlockComment(); } - skipFlowComment(): number | boolean { + skipFlowComment(): number | false { const { pos } = this.state; let shiftToFirstNonWhiteSpace = 2; while ( [charCodes.space, charCodes.tab].includes( + // @ts-expect-error testing whether a number is included this.input.charCodeAt(pos + shiftToFirstNonWhiteSpace), ) ) { @@ -3329,7 +3438,13 @@ export default (superClass: Class): Class => flowEnumErrorBooleanMemberNotInitialized( loc: Position, - { enumName, memberName }: { enumName: string, memberName: string }, + { + enumName, + memberName, + }: { + enumName: string; + memberName: string; + }, ): void { this.raise(FlowErrors.EnumBooleanMemberNotInitialized, { at: loc, @@ -3357,7 +3472,13 @@ export default (superClass: Class): Class => flowEnumErrorNumberMemberNotInitialized( loc: Position, - { enumName, memberName }: { enumName: string, memberName: string }, + { + enumName, + memberName, + }: { + enumName: string; + memberName: string; + }, ): void { this.raise(FlowErrors.EnumNumberMemberNotInitialized, { at: loc, @@ -3368,7 +3489,11 @@ export default (superClass: Class): Class => flowEnumErrorStringMemberInconsistentlyInitailized( node: N.Node, - { enumName }: { enumName: string }, + { + enumName, + }: { + enumName: string; + }, ): void { this.raise(FlowErrors.EnumStringMemberInconsistentlyInitailized, { at: node, @@ -3411,12 +3536,16 @@ export default (superClass: Class): Class => } } - flowEnumMemberRaw(): { id: N.Node, init: EnumMemberInit } { + flowEnumMemberRaw(): { + id: N.Node; + init: EnumMemberInit; + } { const loc = this.state.startLoc; const id = this.parseIdentifier(true); const init = this.eat(tt.eq) ? this.flowEnumMemberInit() : { type: "none", loc }; + // @ts-expect-error: fixme return { id, init }; } @@ -3438,22 +3567,26 @@ export default (superClass: Class): Class => enumName, explicitType, }: { - enumName: string, - explicitType: EnumExplicitType, - }): {| - members: {| - booleanMembers: Array, - numberMembers: Array, - stringMembers: Array, - defaultedMembers: Array, - |}, - hasUnknownMembers: boolean, - |} { + enumName: string; + explicitType: EnumExplicitType; + }): { + members: { + booleanMembers: Array; + numberMembers: Array; + stringMembers: Array; + defaultedMembers: Array; + }; + hasUnknownMembers: boolean; + } { const seenNames = new Set(); const members = { + // @ts-expect-error: migrate to Babel types booleanMembers: [], + // @ts-expect-error: migrate to Babel types numberMembers: [], + // @ts-expect-error: migrate to Babel types stringMembers: [], + // @ts-expect-error: migrate to Babel types defaultedMembers: [], }; let hasUnknownMembers = false; @@ -3547,7 +3680,11 @@ export default (superClass: Class): Class => flowEnumStringMembers( initializedMembers: Array, defaultedMembers: Array, - { enumName }: { enumName: string }, + { + enumName, + }: { + enumName: string; + }, ): Array { if (initializedMembers.length === 0) { return defaultedMembers; @@ -3573,7 +3710,7 @@ export default (superClass: Class): Class => flowEnumParseExplicitType({ enumName, }: { - enumName: string, + enumName: string; }): EnumExplicitType { if (!this.eatContextual(tt._of)) return null; @@ -3603,7 +3740,7 @@ export default (superClass: Class): Class => return value; } - flowEnumBody(node: N.Node, id: N.Node): N.Node { + flowEnumBody(node: Undone, id: N.Node): N.Node { const enumName = id.name; const nameLoc = id.loc.start; const explicitType = this.flowEnumParseExplicitType({ enumName }); @@ -3693,7 +3830,7 @@ export default (superClass: Class): Class => } } - flowParseEnumDeclaration(node: N.Node): N.Node { + flowParseEnumDeclaration(node: Undone): N.Node { const id = this.parseIdentifier(); node.id = id; node.body = this.flowEnumBody(this.startNode(), id); diff --git a/packages/babel-parser/src/plugins/flow/scope.js b/packages/babel-parser/src/plugins/flow/scope.ts similarity index 92% rename from packages/babel-parser/src/plugins/flow/scope.js rename to packages/babel-parser/src/plugins/flow/scope.ts index 418276b59df0..b628761041bd 100644 --- a/packages/babel-parser/src/plugins/flow/scope.js +++ b/packages/babel-parser/src/plugins/flow/scope.ts @@ -1,5 +1,3 @@ -// @flow - import { Position } from "../../util/location"; import ScopeHandler, { Scope } from "../../util/scope"; import { @@ -29,7 +27,7 @@ export default class FlowScopeHandler extends ScopeHandler { return; } - super.declareName(...arguments); + super.declareName(name, bindingType, loc); } isRedeclaredInScope( @@ -37,7 +35,7 @@ export default class FlowScopeHandler extends ScopeHandler { name: string, bindingType: BindingTypes, ): boolean { - if (super.isRedeclaredInScope(...arguments)) return true; + if (super.isRedeclaredInScope(scope, name, bindingType)) return true; if (bindingType & BIND_FLAGS_FLOW_DECLARE_FN) { return ( diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.ts similarity index 93% rename from packages/babel-parser/src/plugins/jsx/index.js rename to packages/babel-parser/src/plugins/jsx/index.ts index f9c2bf9d6c62..6629ae6d0041 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.ts @@ -1,5 +1,3 @@ -// @flow - import * as charCodes from "charcodes"; import XHTMLEntities from "./xhtml"; @@ -17,14 +15,21 @@ import * as N from "../../types"; import { isIdentifierChar, isIdentifierStart } from "../../util/identifier"; import type { Position } from "../../util/location"; import { isNewLine } from "../../util/whitespace"; -import { Errors, ParseErrorEnum } from "../../parse-error"; +import { + Errors, + ParseErrorEnum, + toParseErrorCredentials, +} from "../../parse-error"; +import { type Undone } from "../../parser/node"; /* eslint sort-keys: "error" */ -const JsxErrors = ParseErrorEnum`jsx`(_ => ({ +const JsxErrors = ParseErrorEnum`jsx`((_: typeof toParseErrorCredentials) => ({ AttributeIsEmpty: _( "JSX attributes must only be assigned a non-empty expression.", ), - MissingClosingTagElement: _<{| openingTagName: string |}>( + MissingClosingTagElement: _<{ + openingTagName: string; + }>( ({ openingTagName }) => `Expected corresponding JSX closing tag for <${openingTagName}>.`, ), @@ -35,7 +40,10 @@ const JsxErrors = ParseErrorEnum`jsx`(_ => ({ "Sequence expressions cannot be directly nested inside JSX. Did you mean to wrap it in parentheses (...)?", ), // FIXME: Unify with Errors.UnexpectedToken - UnexpectedToken: _<{| unexpected: string, HTMLEntity: string |}>( + UnexpectedToken: _<{ + unexpected: string; + HTMLEntity: string; + }>( ({ unexpected, HTMLEntity }) => `Unexpected token \`${unexpected}\`. Did you mean \`${HTMLEntity}\` or \`{'${unexpected}'}\`?`, ), @@ -50,7 +58,7 @@ const JsxErrors = ParseErrorEnum`jsx`(_ => ({ /* eslint-disable sort-keys */ -function isFragment(object: ?N.JSXElement): boolean { +function isFragment(object?: N.JSXElement | null): boolean { return object ? object.type === "JSXOpeningFragment" || object.type === "JSXClosingFragment" @@ -82,7 +90,11 @@ function getQualifiedJSXName( throw new Error("Node had unexpected type: " + object.type); } -export default (superClass: Class): Class => +export default (superClass: { + new (...args: any): Parser; +}): { + new (...args: any): Parser; +} => class extends superClass { // Reads inline JSX contents token. @@ -349,7 +361,7 @@ export default (superClass: Class): Class => // Parse JSX spread child - jsxParseSpreadChild(node: N.JSXSpreadChild): N.JSXSpreadChild { + jsxParseSpreadChild(node: Undone): N.JSXSpreadChild { this.next(); // ellipsis node.expression = this.parseExpression(); this.setContext(tc.j_oTag); @@ -362,7 +374,7 @@ export default (superClass: Class): Class => // Parses JSX expression enclosed into curly brackets. jsxParseExpressionContainer( - node: N.JSXExpressionContainer, + node: Undone, previousContext: TokContext, ): N.JSXExpressionContainer { if (this.match(tt.braceR)) { @@ -415,16 +427,22 @@ export default (superClass: Class): Class => startPos: number, startLoc: Position, ): N.JSXOpeningElement { - const node = this.startNodeAt(startPos, startLoc); + const node = this.startNodeAt( + startPos, + startLoc, + ); if (this.eat(tt.jsxTagEnd)) { + // @ts-expect-error migrate to Babel types return this.finishNode(node, "JSXOpeningFragment"); } node.name = this.jsxParseElementName(); - return this.jsxParseOpeningElementAfterName(node); + return this.jsxParseOpeningElementAfterName( + node as Undone, + ); } jsxParseOpeningElementAfterName( - node: N.JSXOpeningElement, + node: Undone, ): N.JSXOpeningElement { const attributes: N.JSXAttribute[] = []; while (!this.match(tt.slash) && !this.match(tt.jsxTagEnd)) { @@ -482,14 +500,21 @@ export default (superClass: Class): Class => break; case tt.braceL: { - const node = this.startNode(); + const node = this.startNode< + N.JSXSpreadChild | N.JSXExpressionContainer + >(); this.setContext(tc.brace); this.next(); if (this.match(tt.ellipsis)) { - children.push(this.jsxParseSpreadChild(node)); + children.push( + this.jsxParseSpreadChild(node as Undone), + ); } else { children.push( - this.jsxParseExpressionContainer(node, tc.j_expr), + this.jsxParseExpressionContainer( + node as Undone, + tc.j_expr, + ), ); } @@ -511,18 +536,15 @@ export default (superClass: Class): Class => }); } else if (!isFragment(openingElement) && isFragment(closingElement)) { this.raise(JsxErrors.MissingClosingTagElement, { - // $FlowIgnore at: closingElement, openingTagName: getQualifiedJSXName(openingElement.name), }); } else if (!isFragment(openingElement) && !isFragment(closingElement)) { if ( - // $FlowIgnore getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name) ) { this.raise(JsxErrors.MissingClosingTagElement, { - // $FlowIgnore at: closingElement, openingTagName: getQualifiedJSXName(openingElement.name), }); @@ -567,7 +589,7 @@ export default (superClass: Class): Class => // Overrides // ================================== - parseExprAtom(refExpressionErrors: ?ExpressionErrors): N.Expression { + parseExprAtom(refExpressionErrors?: ExpressionErrors | null): N.Expression { if (this.match(tt.jsxText)) { return this.parseLiteral(this.state.value, "JSXText"); } else if (this.match(tt.jsxTagStart)) { diff --git a/packages/babel-parser/src/plugins/jsx/xhtml.js b/packages/babel-parser/src/plugins/jsx/xhtml.ts similarity index 98% rename from packages/babel-parser/src/plugins/jsx/xhtml.js rename to packages/babel-parser/src/plugins/jsx/xhtml.ts index 197665044343..d5f80397204e 100644 --- a/packages/babel-parser/src/plugins/jsx/xhtml.js +++ b/packages/babel-parser/src/plugins/jsx/xhtml.ts @@ -1,6 +1,7 @@ -// @flow - -const entities: { [name: string]: string, __proto__: null } = { +const entities: { + __proto__: null; + [name: string]: string; +} = { __proto__: null, quot: "\u0022", amp: "&", diff --git a/packages/babel-parser/src/plugins/placeholders.js b/packages/babel-parser/src/plugins/placeholders.ts similarity index 72% rename from packages/babel-parser/src/plugins/placeholders.js rename to packages/babel-parser/src/plugins/placeholders.ts index 805ff84b48be..486bebe64975 100644 --- a/packages/babel-parser/src/plugins/placeholders.js +++ b/packages/babel-parser/src/plugins/placeholders.ts @@ -1,11 +1,18 @@ -// @flow - import * as charCodes from "charcodes"; import { tokenLabelName, tt } from "../tokenizer/types"; import type Parser from "../parser"; import * as N from "../types"; -import { ParseErrorEnum } from "../parse-error"; +import { ParseErrorEnum, toParseErrorCredentials } from "../parse-error"; +import type { Undone } from "../parser/node"; +import type { Position } from "../util/location"; + +type $Call1 any, A> = F extends ( + a: A, + ...args: any +) => infer R + ? R + : never; export type PlaceholderTypes = | "Identifier" @@ -20,15 +27,14 @@ export type PlaceholderTypes = // $PropertyType doesn't support enums. Use a fake "switch" (GetPlaceholderNode) //type MaybePlaceholder = $PropertyType | N.Placeholder; -type _Switch = $Call< - ( - $ElementType<$ElementType, 0>, - ) => $ElementType<$ElementType, 1>, - Value, +type _Switch = $Call1< + // @ts-expect-error Fixme broken fake switch + (a: Cases[Index][0]) => Cases[Index][1], + Value >; -type $Switch = _Switch; -type NodeOf = $Switch< +type $Switch = _Switch; +type NodeOf = $Switch< T, [ ["Identifier", N.Identifier], @@ -39,25 +45,32 @@ type NodeOf = $Switch< ["BlockStatement", N.BlockStatement], ["ClassBody", N.ClassBody], ["Pattern", N.Pattern], - ], + ] >; // Placeholder breaks everything, because its type is incompatible with // the substituted nodes. -type MaybePlaceholder = NodeOf; // | Placeholder +type MaybePlaceholder = NodeOf; // | Placeholder /* eslint sort-keys: "error" */ -const PlaceholderErrors = ParseErrorEnum`placeholders`(_ => ({ - ClassNameIsRequired: _("A class name is required."), - UnexpectedSpace: _("Unexpected space in placeholder."), -})); +const PlaceholderErrors = ParseErrorEnum`placeholders`( + (_: typeof toParseErrorCredentials) => ({ + ClassNameIsRequired: _("A class name is required."), + UnexpectedSpace: _("Unexpected space in placeholder."), + }), +); /* eslint-disable sort-keys */ -export default (superClass: Class): Class => +export default (superClass: { + new (...args: any): Parser; +}): { + new (...args: any): Parser; +} => + // @ts-expect-error Plugin will override parser interface class extends superClass { - parsePlaceholder( + parsePlaceholder( expectedNode: T, - ): /*?N.Placeholder*/ ?MaybePlaceholder { + ): /*?N.Placeholder*/ MaybePlaceholder | undefined | null { if (this.match(tt.placeholder)) { const node = this.startNode(); this.next(); @@ -69,11 +82,12 @@ export default (superClass: Class): Class => this.assertNoSpace(); this.expect(tt.placeholder); + // @ts-expect-error placeholder typings return this.finishPlaceholder(node, expectedNode); } } - finishPlaceholder( + finishPlaceholder( node: N.Node, expectedNode: T, ): /*N.Placeholder*/ MaybePlaceholder { @@ -94,7 +108,7 @@ export default (superClass: Class): Class => ) { return this.finishOp(tt.placeholder, 2); } - + // @ts-expect-error placeholder typings return super.getTokenFromCode(...arguments); } @@ -108,6 +122,7 @@ export default (superClass: Class): Class => ); } + // @ts-expect-error Plugin will override parser interface parseIdentifier(): MaybePlaceholder<"Identifier"> { // NOTE: This function only handles identifiers outside of // expressions and binding patterns, since they are already @@ -119,28 +134,35 @@ export default (superClass: Class): Class => ); } - checkReservedWord(word: string): void { + checkReservedWord( + word: string, + startLoc: Position, + checkKeywords: boolean, + isBinding: boolean, + ): void { // Sometimes we call #checkReservedWord(node.name), expecting // that node is an Identifier. If it is a Placeholder, name // will be undefined. - if (word !== undefined) super.checkReservedWord(...arguments); + if (word !== undefined) { + super.checkReservedWord(word, startLoc, checkKeywords, isBinding); + } } /* ============================================================ * * parser/lval.js * * ============================================================ */ + // @ts-expect-error Plugin will override parser interface parseBindingAtom(): MaybePlaceholder<"Pattern"> { - return ( - this.parsePlaceholder("Pattern") || super.parseBindingAtom(...arguments) - ); + return this.parsePlaceholder("Pattern") || super.parseBindingAtom(); } - isValidLVal(type: string, ...rest) { + // @ts-expect-error Plugin will override parser interface + isValidLVal(type: string, ...rest: [boolean, BindingTypes]) { return type === "Placeholder" || super.isValidLVal(type, ...rest); } - toAssignable(node: N.Node): void { + toAssignable(node: N.Node, isLHS: boolean): void { if ( node && node.type === "Placeholder" && @@ -148,7 +170,7 @@ export default (superClass: Class): Class => ) { node.expectedNode = "Pattern"; } else { - super.toAssignable(...arguments); + super.toAssignable(node, isLHS); } } @@ -156,7 +178,7 @@ export default (superClass: Class): Class => * parser/statement.js * * ============================================================ */ - isLet(context: ?string): boolean { + isLet(context?: string | null): boolean { if (super.isLet(context)) { return true; } @@ -178,11 +200,16 @@ export default (superClass: Class): Class => return false; } - verifyBreakContinue(node: N.BreakStatement | N.ContinueStatement) { + verifyBreakContinue( + node: N.BreakStatement | N.ContinueStatement, + isBreak: boolean, + ) { + // @ts-expect-error: node.label could be Placeholder if (node.label && node.label.type === "Placeholder") return; - super.verifyBreakContinue(...arguments); + super.verifyBreakContinue(node, isBreak); } + // @ts-expect-error Plugin will override parser interface parseExpressionStatement( node: MaybePlaceholder<"Statement">, expr: N.Expression, @@ -191,38 +218,41 @@ export default (superClass: Class): Class => expr.type !== "Placeholder" || (expr.extra && expr.extra.parenthesized) ) { - return super.parseExpressionStatement(...arguments); + // @ts-expect-error placeholder typings + return super.parseExpressionStatement(node, expr); } if (this.match(tt.colon)) { + // @ts-expect-error const stmt: N.LabeledStatement = node; + // @ts-expect-error: Fixme: placeholder typings stmt.label = this.finishPlaceholder(expr, "Identifier"); this.next(); - stmt.body = this.parseStatement("label"); + stmt.body = super.parseStatement("label"); return this.finishNode(stmt, "LabeledStatement"); } this.semicolon(); - + // @ts-expect-error: Fixme: placeholder typings node.name = expr.name; return this.finishPlaceholder(node, "Statement"); } - + // @ts-expect-error Plugin will override parser interface parseBlock(): MaybePlaceholder<"BlockStatement"> { return ( this.parsePlaceholder("BlockStatement") || super.parseBlock(...arguments) ); } - - parseFunctionId(): ?MaybePlaceholder<"Identifier"> { + // @ts-expect-error Plugin will override parser interface + parseFunctionId(): MaybePlaceholder<"Identifier"> | undefined | null { return ( this.parsePlaceholder("Identifier") || super.parseFunctionId(...arguments) ); } - - parseClass( + // @ts-expect-error Plugin will override parser interface + parseClass( node: T, isStatement: /* T === ClassDeclaration */ boolean, optionalId?: boolean, @@ -240,9 +270,11 @@ export default (superClass: Class): Class => this.match(tt.placeholder) || this.match(tt.braceL) ) { + // @ts-expect-error: placeholder typings node.id = placeholder; } else if (optionalId || !isStatement) { node.id = null; + // @ts-expect-error: placeholder typings node.body = this.finishPlaceholder(placeholder, "ClassBody"); return this.finishNode(node, type); } else { @@ -254,15 +286,17 @@ export default (superClass: Class): Class => this.parseClassId(node, isStatement, optionalId); } - this.parseClassSuper(node); + super.parseClassSuper(node); + // @ts-expect-error placeholder typings node.body = this.parsePlaceholder("ClassBody") || - this.parseClassBody(!!node.superClass, oldStrict); + super.parseClassBody(!!node.superClass, oldStrict); return this.finishNode(node, type); } - + // @ts-expect-error Plugin will override parser interface parseExport(node: N.Node): N.Node { const placeholder = this.parsePlaceholder("Identifier"); + // @ts-expect-error placeholder typings if (!placeholder) return super.parseExport(...arguments); if (!this.isContextual(tt._from) && !this.match(tt.comma)) { @@ -304,13 +338,14 @@ export default (superClass: Class): Class => // "export %%NAME%%" has already been parsed by #parseExport. return true; } - return super.maybeParseExportDefaultSpecifier(...arguments); + return super.maybeParseExportDefaultSpecifier(node); } checkExport(node: N.ExportNamedDeclaration): void { const { specifiers } = node; if (specifiers?.length) { node.specifiers = specifiers.filter( + // @ts-expect-error placeholder typings node => node.exported.type === "Placeholder", ); } @@ -319,25 +354,30 @@ export default (superClass: Class): Class => } parseImport( - node: N.Node, + node: Undone, ): N.ImportDeclaration | N.TsImportEqualsDeclaration { const placeholder = this.parsePlaceholder("Identifier"); + // @ts-expect-error placeholder typings if (!placeholder) return super.parseImport(...arguments); node.specifiers = []; if (!this.isContextual(tt._from) && !this.match(tt.comma)) { // import %%STRING%%; + // @ts-expect-error placeholder typings node.source = this.finishPlaceholder(placeholder, "StringLiteral"); this.semicolon(); return this.finishNode(node, "ImportDeclaration"); } // import %%DEFAULT%% ... - const specifier = this.startNodeAtNode(placeholder); + const specifier = + this.startNodeAtNode(placeholder); + // @ts-expect-error placeholder typings specifier.local = placeholder; - this.finishNode(specifier, "ImportDefaultSpecifier"); - node.specifiers.push(specifier); + node.specifiers.push( + this.finishNode(specifier, "ImportDefaultSpecifier"), + ); if (this.eat(tt.comma)) { // import %%DEFAULT%%, * as ... @@ -348,16 +388,19 @@ export default (superClass: Class): Class => } this.expectContextual(tt._from); + // @ts-expect-error placeholder typings node.source = this.parseImportSource(); this.semicolon(); return this.finishNode(node, "ImportDeclaration"); } + // @ts-expect-error placeholder typings parseImportSource(): MaybePlaceholder<"StringLiteral"> { // import ... from %%STRING%%; return ( this.parsePlaceholder("StringLiteral") || + // @ts-expect-error placeholder typings super.parseImportSource(...arguments) ); } diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.ts similarity index 80% rename from packages/babel-parser/src/plugins/typescript/index.js rename to packages/babel-parser/src/plugins/typescript/index.ts index 90c117cc6637..6a68bb6725bf 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.ts @@ -1,5 +1,3 @@ -// @flow - /*:: declare var invariant; */ import type State from "../../tokenizer/state"; @@ -36,10 +34,14 @@ import TypeScriptScopeHandler from "./scope"; import * as charCodes from "charcodes"; import type { ExpressionErrors } from "../../parser/util"; import { PARAM } from "../../util/production-parameter"; -import { Errors, ParseErrorEnum } from "../../parse-error"; -import { cloneIdentifier } from "../../parser/node"; +import { + Errors, + ParseErrorEnum, + toParseErrorCredentials, +} from "../../parse-error"; +import { cloneIdentifier, type Undone } from "../../parser/node"; -const getOwn = (object, key) => +const getOwn = (object: T, key: keyof T) => Object.hasOwnProperty.call(object, key) && object[key]; type TsModifier = @@ -51,9 +53,8 @@ type TsModifier = | N.Accessibility | N.VarianceAnnotations; -function nonNull(x: ?T): T { +function nonNull(x?: T | null): T { if (x == null) { - // $FlowIgnore throw new Error(`Unexpected ${x} value.`); } return x; @@ -72,194 +73,229 @@ type ParsingContext = | "TypeMembers" | "TypeParametersOrArguments"; +type ModifierBase = { + accessibility?: N.Accessibility; +} & { + [key in TsModifier]?: boolean | undefined | null; +}; + /* eslint sort-keys: "error" */ -const TSErrors = ParseErrorEnum`typescript`(_ => ({ - AbstractMethodHasImplementation: _<{| methodName: string |}>( - ({ methodName }) => - `Method '${methodName}' cannot have an implementation because it is marked abstract.`, - ), - AbstractPropertyHasInitializer: _<{| propertyName: string |}>( - ({ propertyName }) => - `Property '${propertyName}' cannot have an initializer because it is marked abstract.`, - ), - AccesorCannotDeclareThisParameter: _( - "'get' and 'set' accessors cannot declare 'this' parameters.", - ), - AccesorCannotHaveTypeParameters: _( - "An accessor cannot have type parameters.", - ), - CannotFindName: _<{| name: string |}>( - ({ name }) => `Cannot find name '${name}'.`, - ), - ClassMethodHasDeclare: _("Class methods cannot have the 'declare' modifier."), - ClassMethodHasReadonly: _( - "Class methods cannot have the 'readonly' modifier.", - ), - ConstInitiailizerMustBeStringOrNumericLiteralOrLiteralEnumReference: _( - "A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.", - ), - ConstructorHasTypeParameters: _( - "Type parameters cannot appear on a constructor declaration.", - ), - DeclareAccessor: _<{| kind: "get" | "set" |}>( - ({ kind }) => `'declare' is not allowed in ${kind}ters.`, - ), - DeclareClassFieldHasInitializer: _( - "Initializers are not allowed in ambient contexts.", - ), - DeclareFunctionHasImplementation: _( - "An implementation cannot be declared in ambient contexts.", - ), - DuplicateAccessibilityModifier: _<{| modifier: N.Accessibility |}>( - // `Accessibility modifier already seen: ${modifier}` would be more helpful. - // eslint-disable-next-line no-unused-vars - ({ modifier }) => `Accessibility modifier already seen.`, - ), - DuplicateModifier: _<{| modifier: TsModifier |}>( - ({ modifier }) => `Duplicate modifier: '${modifier}'.`, - ), - // `token` matches the terminology used by typescript: - // https://github.com/microsoft/TypeScript/blob/main/src/compiler/types.ts#L2915 - EmptyHeritageClauseType: _<{| token: "extends" | "implements" |}>( - ({ token }) => `'${token}' list cannot be empty.`, - ), - EmptyTypeArguments: _("Type argument list cannot be empty."), - EmptyTypeParameters: _("Type parameter list cannot be empty."), - ExpectedAmbientAfterExportDeclare: _( - "'export declare' must be followed by an ambient declaration.", - ), - ImportAliasHasImportType: _("An import alias can not use 'import type'."), - IncompatibleModifiers: _<{| modifiers: [TsModifier, TsModifier] |}>( - ({ modifiers }) => - `'${modifiers[0]}' modifier cannot be used with '${modifiers[1]}' modifier.`, - ), - IndexSignatureHasAbstract: _( - "Index signatures cannot have the 'abstract' modifier.", - ), - IndexSignatureHasAccessibility: _<{| modifier: N.Accessibility |}>( - ({ modifier }) => - `Index signatures cannot have an accessibility modifier ('${modifier}').`, - ), - IndexSignatureHasDeclare: _( - "Index signatures cannot have the 'declare' modifier.", - ), - IndexSignatureHasOverride: _( - "'override' modifier cannot appear on an index signature.", - ), - IndexSignatureHasStatic: _( - "Index signatures cannot have the 'static' modifier.", - ), - InitializerNotAllowedInAmbientContext: _( - "Initializers are not allowed in ambient contexts.", - ), - InvalidModifierOnTypeMember: _<{| modifier: TsModifier |}>( - ({ modifier }) => `'${modifier}' modifier cannot appear on a type member.`, - ), - InvalidModifierOnTypeParameter: _<{| modifier: TsModifier |}>( - ({ modifier }) => - `'${modifier}' modifier cannot appear on a type parameter.`, - ), - InvalidModifierOnTypeParameterPositions: _<{| modifier: TsModifier |}>( - ({ modifier }) => - `'${modifier}' modifier can only appear on a type parameter of a class, interface or type alias.`, - ), - InvalidModifiersOrder: _<{| orderedModifiers: [TsModifier, TsModifier] |}>( - ({ orderedModifiers }) => - `'${orderedModifiers[0]}' modifier must precede '${orderedModifiers[1]}' modifier.`, - ), - InvalidPropertyAccessAfterInstantiationExpression: _( - "Invalid property access after an instantiation expression. " + - "You can either wrap the instantiation expression in parentheses, or delete the type arguments.", - ), - InvalidTupleMemberLabel: _( - "Tuple members must be labeled with a simple identifier.", - ), - MissingInterfaceName: _( - "'interface' declarations must be followed by an identifier.", - ), - MixedLabeledAndUnlabeledElements: _( - "Tuple members must all have names or all not have names.", - ), - NonAbstractClassHasAbstractMethod: _( - "Abstract methods can only appear within an abstract class.", - ), - NonClassMethodPropertyHasAbstractModifer: _( - "'abstract' modifier can only appear on a class, method, or property declaration.", - ), - OptionalTypeBeforeRequired: _( - "A required element cannot follow an optional element.", - ), - OverrideNotInSubClass: _( - "This member cannot have an 'override' modifier because its containing class does not extend another class.", - ), - PatternIsOptional: _( - "A binding pattern parameter cannot be optional in an implementation signature.", - ), - PrivateElementHasAbstract: _( - "Private elements cannot have the 'abstract' modifier.", - ), - PrivateElementHasAccessibility: _<{| modifier: N.Accessibility |}>( - ({ modifier }) => - `Private elements cannot have an accessibility modifier ('${modifier}').`, - ), - ReadonlyForMethodSignature: _( - "'readonly' modifier can only appear on a property declaration or index signature.", - ), - ReservedArrowTypeParam: _( - "This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma, as in `() => ...`.", - ), - ReservedTypeAssertion: _( - "This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead.", - ), - SetAccesorCannotHaveOptionalParameter: _( - "A 'set' accessor cannot have an optional parameter.", - ), - SetAccesorCannotHaveRestParameter: _( - "A 'set' accessor cannot have rest parameter.", - ), - SetAccesorCannotHaveReturnType: _( - "A 'set' accessor cannot have a return type annotation.", - ), - SingleTypeParameterWithoutTrailingComma: _<{| typeParameterName: string |}>( - ({ typeParameterName }) => - `Single type parameter ${typeParameterName} should have a trailing comma. Example usage: <${typeParameterName},>.`, - ), - StaticBlockCannotHaveModifier: _( - "Static class blocks cannot have any modifier.", - ), - TypeAnnotationAfterAssign: _( - "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`.", - ), - TypeImportCannotSpecifyDefaultAndNamed: _( - "A type-only import can specify a default import or named bindings, but not both.", - ), - TypeModifierIsUsedInTypeExports: _( - "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement.", - ), - TypeModifierIsUsedInTypeImports: _( - "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement.", - ), - UnexpectedParameterModifier: _( - "A parameter property is only allowed in a constructor implementation.", - ), - UnexpectedReadonly: _( - "'readonly' type modifier is only permitted on array and tuple literal types.", - ), - UnexpectedTypeAnnotation: _("Did not expect a type annotation here."), - UnexpectedTypeCastInParameter: _( - "Unexpected type cast in parameter position.", - ), - UnsupportedImportTypeArgument: _( - "Argument in a type import must be a string literal.", - ), - UnsupportedParameterPropertyKind: _( - "A parameter property may not be declared using a binding pattern.", - ), - UnsupportedSignatureParameterKind: _<{| type: string |}>( - ({ type }) => - `Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got ${type}.`, - ), -})); +const TSErrors = ParseErrorEnum`typescript`( + (_: typeof toParseErrorCredentials) => ({ + AbstractMethodHasImplementation: _<{ + methodName: string; + }>( + ({ methodName }) => + `Method '${methodName}' cannot have an implementation because it is marked abstract.`, + ), + AbstractPropertyHasInitializer: _<{ + propertyName: string; + }>( + ({ propertyName }) => + `Property '${propertyName}' cannot have an initializer because it is marked abstract.`, + ), + AccesorCannotDeclareThisParameter: _( + "'get' and 'set' accessors cannot declare 'this' parameters.", + ), + AccesorCannotHaveTypeParameters: _( + "An accessor cannot have type parameters.", + ), + CannotFindName: _<{ + name: string; + }>(({ name }) => `Cannot find name '${name}'.`), + ClassMethodHasDeclare: _( + "Class methods cannot have the 'declare' modifier.", + ), + ClassMethodHasReadonly: _( + "Class methods cannot have the 'readonly' modifier.", + ), + ConstInitiailizerMustBeStringOrNumericLiteralOrLiteralEnumReference: _( + "A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.", + ), + ConstructorHasTypeParameters: _( + "Type parameters cannot appear on a constructor declaration.", + ), + DeclareAccessor: _<{ + kind: "get" | "set"; + }>(({ kind }) => `'declare' is not allowed in ${kind}ters.`), + DeclareClassFieldHasInitializer: _( + "Initializers are not allowed in ambient contexts.", + ), + DeclareFunctionHasImplementation: _( + "An implementation cannot be declared in ambient contexts.", + ), + DuplicateAccessibilityModifier: _<{ + modifier: N.Accessibility; + }>( + // `Accessibility modifier already seen: ${modifier}` would be more helpful. + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ({ modifier }) => `Accessibility modifier already seen.`, + ), + DuplicateModifier: _<{ + modifier: TsModifier; + }>(({ modifier }) => `Duplicate modifier: '${modifier}'.`), + // `token` matches the terminology used by typescript: + // https://github.com/microsoft/TypeScript/blob/main/src/compiler/types.ts#L2915 + EmptyHeritageClauseType: _<{ + token: "extends" | "implements"; + }>(({ token }) => `'${token}' list cannot be empty.`), + EmptyTypeArguments: _("Type argument list cannot be empty."), + EmptyTypeParameters: _("Type parameter list cannot be empty."), + ExpectedAmbientAfterExportDeclare: _( + "'export declare' must be followed by an ambient declaration.", + ), + ImportAliasHasImportType: _("An import alias can not use 'import type'."), + IncompatibleModifiers: _<{ + modifiers: [TsModifier, TsModifier]; + }>( + ({ modifiers }) => + `'${modifiers[0]}' modifier cannot be used with '${modifiers[1]}' modifier.`, + ), + IndexSignatureHasAbstract: _( + "Index signatures cannot have the 'abstract' modifier.", + ), + IndexSignatureHasAccessibility: _<{ + modifier: N.Accessibility; + }>( + ({ modifier }) => + `Index signatures cannot have an accessibility modifier ('${modifier}').`, + ), + IndexSignatureHasDeclare: _( + "Index signatures cannot have the 'declare' modifier.", + ), + IndexSignatureHasOverride: _( + "'override' modifier cannot appear on an index signature.", + ), + IndexSignatureHasStatic: _( + "Index signatures cannot have the 'static' modifier.", + ), + InitializerNotAllowedInAmbientContext: _( + "Initializers are not allowed in ambient contexts.", + ), + InvalidModifierOnTypeMember: _<{ + modifier: TsModifier; + }>( + ({ modifier }) => + `'${modifier}' modifier cannot appear on a type member.`, + ), + InvalidModifierOnTypeParameter: _<{ + modifier: TsModifier; + }>( + ({ modifier }) => + `'${modifier}' modifier cannot appear on a type parameter.`, + ), + InvalidModifierOnTypeParameterPositions: _<{ + modifier: TsModifier; + }>( + ({ modifier }) => + `'${modifier}' modifier can only appear on a type parameter of a class, interface or type alias.`, + ), + InvalidModifiersOrder: _<{ + orderedModifiers: [TsModifier, TsModifier]; + }>( + ({ orderedModifiers }) => + `'${orderedModifiers[0]}' modifier must precede '${orderedModifiers[1]}' modifier.`, + ), + InvalidPropertyAccessAfterInstantiationExpression: _( + "Invalid property access after an instantiation expression. " + + "You can either wrap the instantiation expression in parentheses, or delete the type arguments.", + ), + InvalidTupleMemberLabel: _( + "Tuple members must be labeled with a simple identifier.", + ), + MissingInterfaceName: _( + "'interface' declarations must be followed by an identifier.", + ), + MixedLabeledAndUnlabeledElements: _( + "Tuple members must all have names or all not have names.", + ), + NonAbstractClassHasAbstractMethod: _( + "Abstract methods can only appear within an abstract class.", + ), + NonClassMethodPropertyHasAbstractModifer: _( + "'abstract' modifier can only appear on a class, method, or property declaration.", + ), + OptionalTypeBeforeRequired: _( + "A required element cannot follow an optional element.", + ), + OverrideNotInSubClass: _( + "This member cannot have an 'override' modifier because its containing class does not extend another class.", + ), + PatternIsOptional: _( + "A binding pattern parameter cannot be optional in an implementation signature.", + ), + PrivateElementHasAbstract: _( + "Private elements cannot have the 'abstract' modifier.", + ), + PrivateElementHasAccessibility: _<{ + modifier: N.Accessibility; + }>( + ({ modifier }) => + `Private elements cannot have an accessibility modifier ('${modifier}').`, + ), + ReadonlyForMethodSignature: _( + "'readonly' modifier can only appear on a property declaration or index signature.", + ), + ReservedArrowTypeParam: _( + "This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma, as in `() => ...`.", + ), + ReservedTypeAssertion: _( + "This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead.", + ), + SetAccesorCannotHaveOptionalParameter: _( + "A 'set' accessor cannot have an optional parameter.", + ), + SetAccesorCannotHaveRestParameter: _( + "A 'set' accessor cannot have rest parameter.", + ), + SetAccesorCannotHaveReturnType: _( + "A 'set' accessor cannot have a return type annotation.", + ), + SingleTypeParameterWithoutTrailingComma: _<{ + typeParameterName: string; + }>( + ({ typeParameterName }) => + `Single type parameter ${typeParameterName} should have a trailing comma. Example usage: <${typeParameterName},>.`, + ), + StaticBlockCannotHaveModifier: _( + "Static class blocks cannot have any modifier.", + ), + TypeAnnotationAfterAssign: _( + "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`.", + ), + TypeImportCannotSpecifyDefaultAndNamed: _( + "A type-only import can specify a default import or named bindings, but not both.", + ), + TypeModifierIsUsedInTypeExports: _( + "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement.", + ), + TypeModifierIsUsedInTypeImports: _( + "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement.", + ), + UnexpectedParameterModifier: _( + "A parameter property is only allowed in a constructor implementation.", + ), + UnexpectedReadonly: _( + "'readonly' type modifier is only permitted on array and tuple literal types.", + ), + UnexpectedTypeAnnotation: _("Did not expect a type annotation here."), + UnexpectedTypeCastInParameter: _( + "Unexpected type cast in parameter position.", + ), + UnsupportedImportTypeArgument: _( + "Argument in a type import must be a string literal.", + ), + UnsupportedParameterPropertyKind: _( + "A parameter property may not be declared using a binding pattern.", + ), + UnsupportedSignatureParameterKind: _<{ + type: string; + }>( + ({ type }) => + `Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got ${type}.`, + ), + }), +); /* eslint-disable sort-keys */ @@ -294,19 +330,28 @@ function keywordTypeFromName( } } -function tsIsAccessModifier(modifier: string): boolean %checks { +function tsIsAccessModifier(modifier: string): modifier is N.Accessibility { return ( modifier === "private" || modifier === "public" || modifier === "protected" ); } -function tsIsVarianceAnnotations(modifier: string): boolean %checks { +function tsIsVarianceAnnotations( + modifier: string, +): modifier is N.VarianceAnnotations { return modifier === "in" || modifier === "out"; } -export default (superClass: Class): Class => +export default (superClass: { + new (...args: any): Parser; +}): { + new (...args: any): Parser; +} => + // @ts-expect-error plugin may override interfaces class extends superClass { - getScopeHandler(): Class { + getScopeHandler(): { + new (...args: any): TypeScriptScopeHandler; + } { return TypeScriptScopeHandler; } @@ -338,10 +383,10 @@ export default (superClass: Class): Class => } /** Parses a modifier matching one the given modifier names. */ - tsParseModifier( + tsParseModifier( allowedModifiers: T[], stopOnStartOfClassStaticBlock?: boolean, - ): ?T { + ): T | undefined | null { if (!tokenIsIdentifier(this.state.type) && this.state.type !== tt._in) { return undefined; } @@ -370,17 +415,19 @@ export default (superClass: Class): Class => stopOnStartOfClassStaticBlock, errorTemplate = TSErrors.InvalidModifierOnTypeMember, }: { - modified: { - [key: TsModifier]: ?true, - accessibility?: N.Accessibility, - }, - allowedModifiers: TsModifier[], - disallowedModifiers?: TsModifier[], - stopOnStartOfClassStaticBlock?: boolean, + modified: ModifierBase; + allowedModifiers: readonly TsModifier[]; + disallowedModifiers?: TsModifier[]; + stopOnStartOfClassStaticBlock?: boolean; // FIXME: make sure errorTemplate can receive `modifier` - errorTemplate?: any, + errorTemplate?: any; }): void { - const enforceOrder = (loc, modifier, before, after) => { + const enforceOrder = ( + loc: Position, + modifier: TsModifier, + before: TsModifier, + after: TsModifier, + ) => { if (modifier === before && modified[after]) { this.raise(TSErrors.InvalidModifiersOrder, { at: loc, @@ -388,7 +435,12 @@ export default (superClass: Class): Class => }); } }; - const incompatible = (loc, modifier, mod1, mod2) => { + const incompatible = ( + loc: Position, + modifier: TsModifier, + mod1: TsModifier, + mod2: TsModifier, + ) => { if ( (modified[mod1] && modifier === mod2) || (modified[mod2] && modifier === mod1) @@ -402,7 +454,7 @@ export default (superClass: Class): Class => for (;;) { const { startLoc } = this.state; - const modifier: ?TsModifier = this.tsParseModifier( + const modifier: TsModifier | undefined | null = this.tsParseModifier( allowedModifiers.concat(disallowedModifiers ?? []), stopOnStartOfClassStaticBlock, ); @@ -469,7 +521,10 @@ export default (superClass: Class): Class => throw new Error("Unreachable"); } - tsParseList(kind: ParsingContext, parseElement: () => T): T[] { + tsParseList( + kind: ParsingContext, + parseElement: () => T, + ): T[] { const result: T[] = []; while (!this.tsIsListTerminator(kind)) { // Skipping "parseListElement" from the TS source since that's just for error handling. @@ -478,10 +533,12 @@ export default (superClass: Class): Class => return result; } - tsParseDelimitedList( + tsParseDelimitedList( kind: ParsingContext, parseElement: () => T, - refTrailingCommaPos?: { value: number }, + refTrailingCommaPos?: { + value: number; + }, ): T[] { return nonNull( this.tsParseDelimitedListWorker( @@ -497,12 +554,14 @@ export default (superClass: Class): Class => * If !expectSuccess, returns undefined instead of failing to parse. * If expectSuccess, parseElement should always return a defined value. */ - tsParseDelimitedListWorker( + tsParseDelimitedListWorker( kind: ParsingContext, - parseElement: () => ?T, + parseElement: () => T | undefined | null, expectSuccess: boolean, - refTrailingCommaPos?: { value: number }, - ): ?(T[]) { + refTrailingCommaPos?: { + value: number; + }, + ): T[] | undefined | null { const result = []; let trailingCommaPos = -1; @@ -541,12 +600,14 @@ export default (superClass: Class): Class => return result; } - tsParseBracketedList( + tsParseBracketedList( kind: ParsingContext, parseElement: () => T, bracket: boolean, skipFirstToken: boolean, - refTrailingCommaPos?: { value: number }, + refTrailingCommaPos?: { + value: number; + }, ): T[] { if (!skipFirstToken) { if (bracket) { @@ -572,7 +633,7 @@ export default (superClass: Class): Class => } tsParseImportType(): N.TsImportType { - const node: N.TsImportType = this.startNode(); + const node = this.startNode(); this.expect(tt._import); this.expect(tt.parenL); if (!this.match(tt.string)) { @@ -582,7 +643,7 @@ export default (superClass: Class): Class => } // For compatibility to estree we cannot call parseLiteral directly here - node.argument = this.parseExprAtom(); + node.argument = super.parseExprAtom() as N.StringLiteral; this.expect(tt.parenR); if (this.eat(tt.dot)) { @@ -599,7 +660,8 @@ export default (superClass: Class): Class => tsParseEntityName(allowReservedWords: boolean = true): N.TsEntityName { let entity: N.TsEntityName = this.parseIdentifier(allowReservedWords); while (this.eat(tt.dot)) { - const node: N.TsQualifiedName = this.startNodeAtNode(entity); + const node: Undone = + this.startNodeAtNode(entity); node.left = entity; node.right = this.parseIdentifier(allowReservedWords); entity = this.finishNode(node, "TSQualifiedName"); @@ -608,7 +670,7 @@ export default (superClass: Class): Class => } tsParseTypeReference(): N.TsTypeReference { - const node: N.TsTypeReference = this.startNode(); + const node = this.startNode(); node.typeName = this.tsParseEntityName(); if (!this.hasPrecedingLineBreak() && this.match(tt.lt)) { node.typeParameters = this.tsParseTypeArguments(); @@ -618,7 +680,7 @@ export default (superClass: Class): Class => tsParseThisTypePredicate(lhs: N.TsThisType): N.TsTypePredicate { this.next(); - const node: N.TsTypePredicate = this.startNodeAtNode(lhs); + const node = this.startNodeAtNode(lhs); node.parameterName = lhs; node.typeAnnotation = this.tsParseTypeAnnotation(/* eatColon */ false); node.asserts = false; @@ -626,13 +688,13 @@ export default (superClass: Class): Class => } tsParseThisTypeNode(): N.TsThisType { - const node: N.TsThisType = this.startNode(); + const node = this.startNode(); this.next(); return this.finishNode(node, "TSThisType"); } tsParseTypeQuery(): N.TsTypeQuery { - const node: N.TsTypeQuery = this.startNode(); + const node = this.startNode(); this.expect(tt._typeof); if (this.match(tt._import)) { node.exprName = this.tsParseImportType(); @@ -674,10 +736,10 @@ export default (superClass: Class): Class => tsParseTypeParameter( parseModifiers: ( - node: N.TsTypeParameter, + node: Undone, ) => void = this.tsParseNoneModifiers.bind(this), ): N.TsTypeParameter { - const node: N.TsTypeParameter = this.startNode(); + const node = this.startNode(); parseModifiers(node); @@ -688,15 +750,17 @@ export default (superClass: Class): Class => } tsTryParseTypeParameters( - parseModifiers: ?(node: N.TsTypeParameter) => void, - ): ?N.TsTypeParameterDeclaration { + parseModifiers?: ((node: N.TsTypeParameter) => void) | null, + ): N.TsTypeParameterDeclaration | undefined | null { if (this.match(tt.lt)) { return this.tsParseTypeParameters(parseModifiers); } } - tsParseTypeParameters(parseModifiers: ?(node: N.TsTypeParameter) => void) { - const node: N.TsTypeParameterDeclaration = this.startNode(); + tsParseTypeParameters( + parseModifiers?: ((node: N.TsTypeParameter) => void) | null, + ) { + const node = this.startNode(); if (this.match(tt.lt) || this.match(tt.jsxTagStart)) { this.next(); @@ -708,6 +772,7 @@ export default (superClass: Class): Class => node.params = this.tsParseBracketedList( "TypeParametersOrArguments", + // @ts-expect-error refine typings this.tsParseTypeParameter.bind(this, parseModifiers), /* bracket */ false, /* skipFirstToken */ true, @@ -722,7 +787,7 @@ export default (superClass: Class): Class => return this.finishNode(node, "TSTypeParameterDeclaration"); } - tsTryNextParseConstantContext(): ?N.TsTypeReference { + tsTryNextParseConstantContext(): N.TsTypeReference | undefined | null { if (this.lookahead().type !== tt._const) return null; this.next(); @@ -746,7 +811,7 @@ export default (superClass: Class): Class => // but here it's always false, because this is only used for types. tsFillSignature( returnToken: TokenType, - signature: N.TsSignatureDeclaration, + signature: Undone, ): void { // Arrow fns *must* have return token (`=>`). Normal functions can omit it. const returnTokenRequired = returnToken === tt.arrow; @@ -769,11 +834,12 @@ export default (superClass: Class): Class => } } - tsParseBindingListForSignature(): $ReadOnlyArray< - N.Identifier | N.RestElement | N.ObjectPattern | N.ArrayPattern, + tsParseBindingListForSignature(): Array< + N.Identifier | N.RestElement | N.ObjectPattern | N.ArrayPattern > { - return this.parseBindingList(tt.parenR, charCodes.rightParenthesis).map( - pattern => { + return super + .parseBindingList(tt.parenR, charCodes.rightParenthesis) + .map(pattern => { if ( pattern.type !== "Identifier" && pattern.type !== "RestElement" && @@ -785,9 +851,8 @@ export default (superClass: Class): Class => type: pattern.type, }); } - return (pattern: any); - }, - ); + return pattern as any; + }); } tsParseTypeMemberSemicolon(): void { @@ -798,7 +863,9 @@ export default (superClass: Class): Class => tsParseSignatureMember( kind: "TSCallSignatureDeclaration" | "TSConstructSignatureDeclaration", - node: N.TsCallSignatureDeclaration | N.TsConstructSignatureDeclaration, + node: Undone< + N.TsCallSignatureDeclaration | N.TsConstructSignatureDeclaration + >, ): N.TsCallSignatureDeclaration | N.TsConstructSignatureDeclaration { this.tsFillSignature(tt.colon, node); this.tsParseTypeMemberSemicolon(); @@ -814,7 +881,9 @@ export default (superClass: Class): Class => return false; } - tsTryParseIndexSignature(node: N.Node): ?N.TsIndexSignature { + tsTryParseIndexSignature( + node: Undone, + ): N.TsIndexSignature | undefined | null { if ( !( this.match(tt.bracketL) && @@ -923,7 +992,7 @@ export default (superClass: Class): Class => } if (this.match(tt._new)) { - const id: N.Identifier = this.startNode(); + const id = this.startNode(); this.next(); if (this.match(tt.parenL) || this.match(tt.lt)) { return this.tsParseSignatureMember( @@ -955,7 +1024,7 @@ export default (superClass: Class): Class => return idx; } - this.parsePropertyName(node); + super.parsePropertyName(node); if ( !node.computed && node.key.type === "Identifier" && @@ -963,18 +1032,18 @@ export default (superClass: Class): Class => this.tsTokenCanFollowModifier() ) { node.kind = node.key.name; - this.parsePropertyName(node); + super.parsePropertyName(node); } return this.tsParsePropertyOrMethodSignature(node, !!node.readonly); } tsParseTypeLiteral(): N.TsTypeLiteral { - const node: N.TsTypeLiteral = this.startNode(); + const node = this.startNode(); node.members = this.tsParseObjectTypeMembers(); return this.finishNode(node, "TSTypeLiteral"); } - tsParseObjectTypeMembers(): $ReadOnlyArray { + tsParseObjectTypeMembers(): Array { this.expect(tt.braceL); const members = this.tsParseList( "TypeMembers", @@ -1004,14 +1073,14 @@ export default (superClass: Class): Class => } tsParseMappedTypeParameter(): N.TsTypeParameter { - const node: N.TsTypeParameter = this.startNode(); + const node = this.startNode(); node.name = this.tsParseTypeParameterName(); node.constraint = this.tsExpectThenParseType(tt._in); return this.finishNode(node, "TSTypeParameter"); } tsParseMappedType(): N.TsMappedType { - const node: N.TsMappedType = this.startNode(); + const node = this.startNode(); this.expect(tt.braceL); @@ -1045,7 +1114,7 @@ export default (superClass: Class): Class => } tsParseTupleType(): N.TsTupleType { - const node: N.TsTupleType = this.startNode(); + const node = this.startNode(); node.elementTypes = this.tsParseBracketedList( "TupleElementTypes", this.tsParseTupleElementType.bind(this), @@ -1056,9 +1125,9 @@ export default (superClass: Class): Class => // Validate the elementTypes to ensure that no mandatory elements // follow optional elements let seenOptionalElement = false; - let labeledElements = null; + let labeledElements: boolean | null = null; node.elementTypes.forEach(elementNode => { - let { type } = elementNode; + const { type } = elementNode; if ( seenOptionalElement && @@ -1078,12 +1147,13 @@ export default (superClass: Class): Class => type === "TSOptionalType"; // When checking labels, check the argument of the spread operator + let checkType = type; if (type === "TSRestType") { elementNode = elementNode.typeAnnotation; - type = elementNode.type; + checkType = elementNode.type; } - const isLabeled = type === "TSNamedTupleMember"; + const isLabeled = checkType === "TSNamedTupleMember"; // Flow doesn't support ??= labeledElements = labeledElements ?? isLabeled; if (labeledElements !== isLabeled) { @@ -1102,12 +1172,12 @@ export default (superClass: Class): Class => const { start: startPos, startLoc } = this.state; const rest = this.eat(tt.ellipsis); - let type = this.tsParseType(); + let type: N.TsType | N.TsNamedTupleMember = this.tsParseType(); const optional = this.eat(tt.question); const labeled = this.eat(tt.colon); if (labeled) { - const labeledNode: N.TsNamedTupleMember = this.startNodeAtNode(type); + const labeledNode = this.startNodeAtNode(type); labeledNode.optional = optional; if ( @@ -1115,25 +1185,25 @@ export default (superClass: Class): Class => !type.typeParameters && type.typeName.type === "Identifier" ) { - labeledNode.label = (type.typeName: N.Identifier); + labeledNode.label = type.typeName as N.Identifier; } else { this.raise(TSErrors.InvalidTupleMemberLabel, { at: type }); // This produces an invalid AST, but at least we don't drop // nodes representing the invalid source. - // $FlowIgnore + // @ts-expect-error labeledNode.label = type; } labeledNode.elementType = this.tsParseType(); type = this.finishNode(labeledNode, "TSNamedTupleMember"); } else if (optional) { - const optionalTypeNode: N.TsOptionalType = this.startNodeAtNode(type); + const optionalTypeNode = this.startNodeAtNode(type); optionalTypeNode.typeAnnotation = type; type = this.finishNode(optionalTypeNode, "TSOptionalType"); } if (rest) { - const restNode: N.TsRestType = this.startNodeAt(startPos, startLoc); + const restNode = this.startNodeAt(startPos, startLoc); restNode.typeAnnotation = type; type = this.finishNode(restNode, "TSRestType"); } @@ -1142,7 +1212,7 @@ export default (superClass: Class): Class => } tsParseParenthesizedType(): N.TsParenthesizedType { - const node = this.startNode(); + const node = this.startNode(); this.expect(tt.parenL); node.typeAnnotation = this.tsParseType(); this.expect(tt.parenR); @@ -1153,9 +1223,11 @@ export default (superClass: Class): Class => type: "TSFunctionType" | "TSConstructorType", abstract?: boolean, ): N.TsFunctionOrConstructorType { - const node: N.TsFunctionOrConstructorType = this.startNode(); + const node = this.startNode< + N.TsFunctionOrConstructorType | N.TsConstructorType + >(); if (type === "TSConstructorType") { - // $FlowIgnore + // @ts-expect-error node.abstract = !!abstract; if (abstract) this.next(); this.next(); // eat `new` @@ -1167,7 +1239,8 @@ export default (superClass: Class): Class => } tsParseLiteralTypeNode(): N.TsLiteralType { - const node: N.TsLiteralType = this.startNode(); + const node = this.startNode(); + // @ts-expect-error refine typings node.literal = (() => { switch (this.state.type) { case tt.num: @@ -1176,7 +1249,7 @@ export default (superClass: Class): Class => case tt._true: case tt._false: // For compatibility to estree we cannot call parseLiteral directly here - return this.parseExprAtom(); + return super.parseExprAtom(); default: throw this.unexpected(); } @@ -1185,12 +1258,12 @@ export default (superClass: Class): Class => } tsParseTemplateLiteralType(): N.TsType { - const node: N.TsLiteralType = this.startNode(); - node.literal = this.parseTemplate(false); + const node = this.startNode(); + node.literal = super.parseTemplate(false); return this.finishNode(node, "TSLiteralType"); } - parseTemplateSubstitution(): N.TsType { + parseTemplateSubstitution(): N.TsType | N.Node { if (this.state.inType) return this.tsParseType(); return super.parseTemplateSubstitution(); } @@ -1214,11 +1287,12 @@ export default (superClass: Class): Class => return this.tsParseLiteralTypeNode(); case tt.plusMin: if (this.state.value === "-") { - const node: N.TsLiteralType = this.startNode(); + const node = this.startNode(); const nextToken = this.lookahead(); if (nextToken.type !== tt.num && nextToken.type !== tt.bigint) { throw this.unexpected(); } + // @ts-expect-error: parseMaybeUnary must returns unary expression node.literal = this.parseMaybeUnary(); return this.finishNode(node, "TSLiteralType"); } @@ -1269,7 +1343,7 @@ export default (superClass: Class): Class => nodeType !== undefined && this.lookaheadCharCode() !== charCodes.dot ) { - const node: N.TsKeywordType = this.startNode(); + const node = this.startNode(); this.next(); return this.finishNode(node, nodeType); } @@ -1285,12 +1359,12 @@ export default (superClass: Class): Class => let type = this.tsParseNonArrayType(); while (!this.hasPrecedingLineBreak() && this.eat(tt.bracketL)) { if (this.match(tt.bracketR)) { - const node: N.TsArrayType = this.startNodeAtNode(type); + const node = this.startNodeAtNode(type); node.elementType = type; this.expect(tt.bracketR); type = this.finishNode(node, "TSArrayType"); } else { - const node: N.TsIndexedAccessType = this.startNodeAtNode(type); + const node = this.startNodeAtNode(type); node.objectType = type; node.indexType = this.tsParseType(); this.expect(tt.bracketR); @@ -1301,7 +1375,7 @@ export default (superClass: Class): Class => } tsParseTypeOperator(): N.TsTypeOperator { - const node: N.TsTypeOperator = this.startNode(); + const node = this.startNode(); const operator = this.state.value; this.next(); // eat operator node.operator = operator; @@ -1325,9 +1399,9 @@ export default (superClass: Class): Class => } tsParseInferType(): N.TsInferType { - const node = this.startNode(); + const node = this.startNode(); this.expectContextual(tt._infer); - const typeParameter = this.startNode(); + const typeParameter = this.startNode(); typeParameter.name = this.tsParseTypeParameterName(); typeParameter.constraint = this.tsTryParse(() => this.tsParseConstraintForInferType(), @@ -1367,7 +1441,7 @@ export default (superClass: Class): Class => parseConstituentType: () => N.TsType, operator: TokenType, ): N.TsType { - const node: N.TsUnionType | N.TsIntersectionType = this.startNode(); + const node = this.startNode(); const hasLeadingOperator = this.eat(operator); const types = []; do { @@ -1430,7 +1504,7 @@ export default (superClass: Class): Class => const { errors } = this.state; const previousErrorCount = errors.length; try { - this.parseBindingList( + super.parseBindingList( tt.bracketR, charCodes.rightSquareBracket, true, @@ -1479,7 +1553,7 @@ export default (superClass: Class): Class => returnToken: TokenType, ): N.TsTypeAnnotation { return this.tsInType(() => { - const t: N.TsTypeAnnotation = this.startNode(); + const t = this.startNode(); this.expect(returnToken); const node = this.startNode(); @@ -1495,13 +1569,13 @@ export default (superClass: Class): Class => // if it turns out to be a `TSThisType`, wrap it with `TSTypePredicate` // : asserts this if (thisTypePredicate.type === "TSThisType") { - node.parameterName = (thisTypePredicate: N.TsThisType); + node.parameterName = thisTypePredicate as N.TsThisType; node.asserts = true; - (node: N.TsTypePredicate).typeAnnotation = null; + (node as N.TsTypePredicate).typeAnnotation = null; thisTypePredicate = this.finishNode(node, "TSTypePredicate"); } else { this.resetStartLocationFromNode(thisTypePredicate, node); - (thisTypePredicate: N.TsTypePredicate).asserts = true; + (thisTypePredicate as N.TsTypePredicate).asserts = true; } t.typeAnnotation = thisTypePredicate; return this.finishNode(t, "TSTypeAnnotation"); @@ -1520,7 +1594,7 @@ export default (superClass: Class): Class => // : asserts foo node.parameterName = this.parseIdentifier(); node.asserts = asserts; - (node: N.TsTypePredicate).typeAnnotation = null; + (node as N.TsTypePredicate).typeAnnotation = null; t.typeAnnotation = this.finishNode(node, "TSTypePredicate"); return this.finishNode(t, "TSTypeAnnotation"); } @@ -1535,21 +1609,24 @@ export default (superClass: Class): Class => }); } - tsTryParseTypeOrTypePredicateAnnotation(): ?N.TsTypeAnnotation { + tsTryParseTypeOrTypePredicateAnnotation(): + | N.TsTypeAnnotation + | undefined + | null { return this.match(tt.colon) ? this.tsParseTypeOrTypePredicateAnnotation(tt.colon) : undefined; } - tsTryParseTypeAnnotation(): ?N.TsTypeAnnotation { + tsTryParseTypeAnnotation(): N.TsTypeAnnotation | undefined | null { return this.match(tt.colon) ? this.tsParseTypeAnnotation() : undefined; } - tsTryParseType(): ?N.TsType { + tsTryParseType(): N.TsType | undefined | null { return this.tsEatThenParseType(tt.colon); } - tsParseTypePredicatePrefix(): ?N.Identifier { + tsParseTypePredicatePrefix(): N.Identifier | undefined | null { const id = this.parseIdentifier(); if (this.isContextual(tt._is) && !this.hasPrecedingLineBreak()) { this.next(); @@ -1579,7 +1656,7 @@ export default (superClass: Class): Class => tsParseTypeAnnotation( eatColon = true, - t: N.TsTypeAnnotation = this.startNode(), + t: Undone = this.startNode(), ): N.TsTypeAnnotation { this.tsInType(() => { if (eatColon) this.expect(tt.colon); @@ -1601,7 +1678,7 @@ export default (superClass: Class): Class => ) { return type; } - const node: N.TsConditionalType = this.startNodeAtNode(type); + const node = this.startNodeAtNode(type); node.checkType = type; node.extendsType = this.tsInDisallowConditionalTypesContext(() => @@ -1649,7 +1726,7 @@ export default (superClass: Class): Class => this.raise(TSErrors.ReservedTypeAssertion, { at: this.state.startLoc }); } - const node: N.TsTypeAssertion = this.startNode(); + const node = this.startNode(); const _const = this.tsTryNextParseConstantContext(); node.typeAnnotation = _const || this.tsNextThenParseType(); this.expect(tt.gt); @@ -1659,13 +1736,13 @@ export default (superClass: Class): Class => tsParseHeritageClause( token: "extends" | "implements", - ): $ReadOnlyArray { + ): Array { const originalStartLoc = this.state.startLoc; const delimitedList = this.tsParseDelimitedList( "HeritageClauseElement", () => { - const node: N.TsExpressionWithTypeArguments = this.startNode(); + const node = this.startNode(); node.expression = this.tsParseEntityName(); if (this.match(tt.lt)) { node.typeParameters = this.tsParseTypeArguments(); @@ -1686,9 +1763,11 @@ export default (superClass: Class): Class => } tsParseInterfaceDeclaration( - node: N.TsInterfaceDeclaration, - properties: { declare?: true } = {}, - ): ?N.TsInterfaceDeclaration { + node: Undone, + properties: { + declare?: true; + } = {}, + ): N.TsInterfaceDeclaration | undefined | null { if (this.hasFollowingLineBreak()) return null; this.expectContextual(tt._interface); if (properties.declare) node.declare = true; @@ -1706,7 +1785,7 @@ export default (superClass: Class): Class => if (this.eat(tt._extends)) { node.extends = this.tsParseHeritageClause("extends"); } - const body: N.TSInterfaceBody = this.startNode(); + const body = this.startNode(); body.body = this.tsInType(this.tsParseObjectTypeMembers.bind(this)); node.body = this.finishNode(body, "TSInterfaceBody"); return this.finishNode(node, "TSInterfaceDeclaration"); @@ -1729,7 +1808,7 @@ export default (superClass: Class): Class => this.isContextual(tt._intrinsic) && this.lookahead().type !== tt.dot ) { - const node: N.TsKeywordType = this.startNode(); + const node = this.startNode(); this.next(); return this.finishNode(node, "TSIntrinsicKeyword"); } @@ -1810,20 +1889,23 @@ export default (superClass: Class): Class => } tsParseEnumMember(): N.TsEnumMember { - const node: N.TsEnumMember = this.startNode(); + const node = this.startNode(); // Computed property names are grammar errors in an enum, so accept just string literal or identifier. node.id = this.match(tt.string) - ? this.parseExprAtom() + ? super.parseStringLiteral(this.state.value) : this.parseIdentifier(/* liberal */ true); if (this.eat(tt.eq)) { - node.initializer = this.parseMaybeAssignAllowIn(); + node.initializer = super.parseMaybeAssignAllowIn(); } return this.finishNode(node, "TSEnumMember"); } tsParseEnumDeclaration( - node: N.TsEnumDeclaration, - properties: { const?: true, declare?: true } = {}, + node: Undone, + properties: { + const?: true; + declare?: true; + } = {}, ): N.TsEnumDeclaration { if (properties.const) node.const = true; if (properties.declare) node.declare = true; @@ -1844,12 +1926,12 @@ export default (superClass: Class): Class => } tsParseModuleBlock(): N.TsModuleBlock { - const node: N.TsModuleBlock = this.startNode(); + const node = this.startNode(); this.scope.enter(SCOPE_OTHER); this.expect(tt.braceL); // Inside of a module block is considered "top-level", meaning it can have imports and exports. - this.parseBlockOrModuleBlockBody( + super.parseBlockOrModuleBlockBody( (node.body = []), /* directives */ undefined, /* topLevel */ true, @@ -1860,8 +1942,8 @@ export default (superClass: Class): Class => } tsParseModuleOrNamespaceDeclaration( - node: N.TsModuleDeclaration, - nested?: boolean = false, + node: Undone, + nested: boolean = false, ): N.TsModuleDeclaration { node.id = this.parseIdentifier(); @@ -1870,8 +1952,9 @@ export default (superClass: Class): Class => } if (this.eat(tt.dot)) { - const inner = this.startNode(); + const inner = this.startNode(); this.tsParseModuleOrNamespaceDeclaration(inner, true); + // @ts-expect-error Fixme: refine typings node.body = inner; } else { this.scope.enter(SCOPE_TS_MODULE); @@ -1890,7 +1973,7 @@ export default (superClass: Class): Class => node.global = true; node.id = this.parseIdentifier(); } else if (this.match(tt.string)) { - node.id = this.parseExprAtom(); + node.id = super.parseStringLiteral(this.state.value); } else { this.unexpected(); } @@ -1908,7 +1991,7 @@ export default (superClass: Class): Class => } tsParseImportEqualsDeclaration( - node: N.TsImportEqualsDeclaration, + node: Undone, isExport?: boolean, ): N.TsImportEqualsDeclaration { node.isExport = isExport || false; @@ -1943,14 +2026,14 @@ export default (superClass: Class): Class => } tsParseExternalModuleReference(): N.TsExternalModuleReference { - const node: N.TsExternalModuleReference = this.startNode(); + const node = this.startNode(); this.expectContextual(tt._require); this.expect(tt.parenL); if (!this.match(tt.string)) { throw this.unexpected(); } // For compatibility to estree we cannot call parseLiteral directly here - node.expression = this.parseExprAtom(); + node.expression = super.parseExprAtom() as N.StringLiteral; this.expect(tt.parenR); return this.finishNode(node, "TSExternalModuleReference"); } @@ -1964,15 +2047,18 @@ export default (superClass: Class): Class => return res; } - tsTryParseAndCatch(f: () => T): ?T { + tsTryParseAndCatch( + f: () => T, + ): T | undefined | null { const result = this.tryParse(abort => f() || abort()); if (result.aborted || !result.node) return undefined; if (result.error) this.state = result.failState; + // @ts-expect-error refine typings return result.node; } - tsTryParse(f: () => ?T): ?T { + tsTryParse(f: () => T | undefined | false): T | undefined { const state = this.state.clone(); const result = f(); if (result !== undefined && result !== false) { @@ -1983,22 +2069,23 @@ export default (superClass: Class): Class => } } - tsTryParseDeclare(nany: any): ?N.Declaration { + tsTryParseDeclare(nany: any): N.Declaration | undefined | null { if (this.isLineTerminator()) { return; } let starttype = this.state.type; - let kind; + let kind: "let" | null; if (this.isContextual(tt._let)) { starttype = tt._var; - kind = "let"; + kind = "let" as const; } + // @ts-expect-error refine typings return this.tsInAmbientContext(() => { if (starttype === tt._function) { nany.declare = true; - return this.parseFunctionStatement( + return super.parseFunctionStatement( nany, /* async */ false, /* declarationPosition */ true, @@ -2056,7 +2143,7 @@ export default (superClass: Class): Class => } // Note: this won't be called unless the keyword is allowed in `shouldParseExportDeclaration`. - tsTryParseExportDeclaration(): ?N.Declaration { + tsTryParseExportDeclaration(): N.Declaration | undefined | null { return this.tsParseDeclaration( this.startNode(), this.state.value, @@ -2064,7 +2151,10 @@ export default (superClass: Class): Class => ); } - tsParseExpressionStatement(node: any, expr: N.Identifier): ?N.Declaration { + tsParseExpressionStatement( + node: Undone, + expr: N.Identifier, + ): N.Declaration | undefined | null { switch (expr.name) { case "declare": { const declaration = this.tsTryParseDeclare(node); @@ -2080,7 +2170,7 @@ export default (superClass: Class): Class => if (this.match(tt.braceL)) { this.scope.enter(SCOPE_TS_MODULE); this.prodParam.enter(PARAM); - const mod: N.TsModuleDeclaration = node; + const mod = node as Undone; mod.global = true; mod.id = expr; mod.body = this.tsParseModuleBlock(); @@ -2100,7 +2190,7 @@ export default (superClass: Class): Class => node: any, value: string, next: boolean, - ): ?N.Declaration { + ): N.Declaration | undefined | null { // no declaration apart from enum can be followed by a line break. switch (value) { case "abstract": @@ -2154,7 +2244,7 @@ export default (superClass: Class): Class => tsTryParseGenericAsyncArrowFunction( startPos: number, startLoc: Position, - ): ?N.ArrowFunctionExpression { + ): N.ArrowFunctionExpression | undefined | null { if (!this.match(tt.lt)) { return undefined; } @@ -2162,18 +2252,19 @@ export default (superClass: Class): Class => const oldMaybeInArrowParameters = this.state.maybeInArrowParameters; this.state.maybeInArrowParameters = true; - const res: ?N.ArrowFunctionExpression = this.tsTryParseAndCatch(() => { - const node: N.ArrowFunctionExpression = this.startNodeAt( - startPos, - startLoc, - ); - node.typeParameters = this.tsParseTypeParameters(); - // Don't use overloaded parseFunctionParams which would look for "<" again. - super.parseFunctionParams(node); - node.returnType = this.tsTryParseTypeOrTypePredicateAnnotation(); - this.expect(tt.arrow); - return node; - }); + const res: Undone | undefined | null = + this.tsTryParseAndCatch(() => { + const node = this.startNodeAt( + startPos, + startLoc, + ); + node.typeParameters = this.tsParseTypeParameters(); + // Don't use overloaded parseFunctionParams which would look for "<" again. + super.parseFunctionParams(node); + node.returnType = this.tsTryParseTypeOrTypePredicateAnnotation(); + this.expect(tt.arrow); + return node; + }); this.state.maybeInArrowParameters = oldMaybeInArrowParameters; @@ -2181,7 +2272,7 @@ export default (superClass: Class): Class => return undefined; } - return this.parseArrowExpression( + return super.parseArrowExpression( res, /* params are already set */ null, /* async */ true, @@ -2198,7 +2289,7 @@ export default (superClass: Class): Class => } tsParseTypeArguments(): N.TsTypeParameterInstantiation { - const node = this.startNode(); + const node = this.startNode(); node.params = this.tsInType(() => // Temporarily remove a JSX parsing context, which makes us scan different tokens. this.tsInNoContext(() => { @@ -2230,18 +2321,18 @@ export default (superClass: Class): Class => } parseAssignableListItem( - allowModifiers: ?boolean, + allowModifiers: boolean | undefined | null, decorators: N.Decorator[], ): N.Pattern | N.TSParameterProperty { // Store original location/position to include modifiers in range const startPos = this.state.start; const startLoc = this.state.startLoc; - let accessibility: ?N.Accessibility; + let accessibility: N.Accessibility | undefined | null; let readonly = false; let override = false; if (allowModifiers !== undefined) { - const modified = {}; + const modified: ModifierBase = {}; this.tsParseModifiers({ modified, allowedModifiers: [ @@ -2267,7 +2358,7 @@ export default (superClass: Class): Class => this.parseAssignableListItemTypes(left); const elt = this.parseMaybeDefault(left.start, left.loc.start, left); if (accessibility || readonly || override) { - const pp: N.TSParameterProperty = this.startNodeAt(startPos, startLoc); + const pp = this.startNodeAt(startPos, startLoc); if (decorators.length) { pp.decorators = decorators; } @@ -2277,7 +2368,7 @@ export default (superClass: Class): Class => if (elt.type !== "Identifier" && elt.type !== "AssignmentPattern") { this.raise(TSErrors.UnsupportedParameterPropertyKind, { at: pp }); } - pp.parameter = ((elt: any): N.Identifier | N.AssignmentPattern); + pp.parameter = elt as any as N.Identifier | N.AssignmentPattern; return this.finishNode(pp, "TSParameterProperty"); } @@ -2288,7 +2379,7 @@ export default (superClass: Class): Class => return elt; } - isSimpleParameter(node) { + isSimpleParameter(node: N.Pattern | N.TSParameterProperty) { return ( (node.type === "TSParameterProperty" && super.isSimpleParameter(node.parameter)) || @@ -2296,11 +2387,13 @@ export default (superClass: Class): Class => ); } - parseFunctionBodyAndFinish( - node: N.BodilessFunctionOrMethodBase, - type: string, - isMethod?: boolean = false, - ): void { + parseFunctionBodyAndFinish< + T extends + | N.Function + | N.TSDeclareMethod + | N.TSDeclareFunction + | N.ClassPrivateMethod, + >(node: Undone, type: T["type"], isMethod: boolean = false): T { if (this.match(tt.colon)) { node.returnType = this.tsParseTypeOrTypePredicateAnnotation(tt.colon); } @@ -2312,21 +2405,19 @@ export default (superClass: Class): Class => ? "TSDeclareMethod" : undefined; if (bodilessType && !this.match(tt.braceL) && this.isLineTerminator()) { - this.finishNode(node, bodilessType); - return; + return this.finishNode(node, bodilessType); } if (bodilessType === "TSDeclareFunction" && this.state.isAmbientContext) { this.raise(TSErrors.DeclareFunctionHasImplementation, { at: node }); if ( - // $FlowIgnore + // @ts-expect-error node.declare ) { - super.parseFunctionBodyAndFinish(node, bodilessType, isMethod); - return; + return super.parseFunctionBodyAndFinish(node, bodilessType, isMethod); } } - super.parseFunctionBodyAndFinish(node, type, isMethod); + return super.parseFunctionBodyAndFinish(node, type, isMethod); } registerFunctionStatementId(node: N.Function): void { @@ -2335,11 +2426,11 @@ export default (superClass: Class): Class => // For bodyless function, we need to do it here. this.checkIdentifier(node.id, BIND_TS_AMBIENT); } else { - super.registerFunctionStatementId(...arguments); + super.registerFunctionStatementId(node); } } - tsCheckForInvalidTypeCasts(items: $ReadOnlyArray) { + tsCheckForInvalidTypeCasts(items: Array) { items.forEach(node => { if (node?.type === "TSTypeCastExpression") { this.raise(TSErrors.UnexpectedTypeAnnotation, { @@ -2350,9 +2441,10 @@ export default (superClass: Class): Class => } toReferencedList( - exprList: $ReadOnlyArray, - isInParens?: boolean, // eslint-disable-line no-unused-vars - ): $ReadOnlyArray { + exprList: Array, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + isInParens?: boolean, + ): Array { // Handles invalid scenarios like: `f(a:b)`, `(a:b);`, and `(a:b,c:d)`. // // Note that `f(a:b)` goes through a different path and is handled @@ -2361,7 +2453,9 @@ export default (superClass: Class): Class => return exprList; } - parseArrayLike(...args): N.ArrayExpression | N.TupleExpression { + parseArrayLike( + ...args: [TokenType, boolean, boolean, ExpressionErrors | null] + ): N.ArrayExpression | N.TupleExpression { const node = super.parseArrayLike(...args); if (node.type === "ArrayExpression") { @@ -2375,7 +2469,7 @@ export default (superClass: Class): Class => base: N.Expression, startPos: number, startLoc: Position, - noCalls: ?boolean, + noCalls: boolean | undefined | null, state: N.ParseSubscriptState, ): N.Expression { if (!this.hasPrecedingLineBreak() && this.match(tt.bang)) { @@ -2385,7 +2479,7 @@ export default (superClass: Class): Class => this.state.canStartJSXElement = false; this.next(); - const nonNullExpression: N.TsNonNullExpression = this.startNodeAt( + const nonNullExpression = this.startNodeAt( startPos, startLoc, ); @@ -2434,7 +2528,7 @@ export default (superClass: Class): Class => } if (tokenIsTemplate(this.state.type)) { - const result = this.parseTaggedTemplateExpression( + const result = super.parseTaggedTemplateExpression( base, startPos, startLoc, @@ -2445,10 +2539,10 @@ export default (superClass: Class): Class => } if (!noCalls && this.eat(tt.parenL)) { - const node: N.CallExpression = this.startNodeAt(startPos, startLoc); + const node = this.startNodeAt(startPos, startLoc); node.callee = base; // possibleAsync always false here, because we would have handled it above. - // $FlowIgnore (won't be any undefined arguments) + // @ts-expect-error (won't be any undefined arguments) node.arguments = this.parseCallExpressionArguments( tt.parenR, /* possibleAsync */ false, @@ -2459,7 +2553,7 @@ export default (superClass: Class): Class => node.typeParameters = typeArguments; if (state.optionalChainMember) { - // $FlowIgnore + // @ts-expect-error node.optional = isOptionalCall; } @@ -2479,7 +2573,7 @@ export default (superClass: Class): Class => return; } - const node: N.TsInstantiationExpression = this.startNodeAt( + const node = this.startNodeAt( startPos, startLoc, ); @@ -2519,7 +2613,9 @@ export default (superClass: Class): Class => callee.type === "TSInstantiationExpression" && !callee.extra?.parenthesized ) { + // @ts-expect-error migrate to Babel types node.typeParameters = callee.typeParameters; + // @ts-expect-error migrate to Babel types node.callee = callee.expression; } } @@ -2529,13 +2625,13 @@ export default (superClass: Class): Class => leftStartPos: number, leftStartLoc: Position, minPrec: number, - ) { + ): N.Expression { if ( tokenOperatorPrecedence(tt._in) > minPrec && !this.hasPrecedingLineBreak() && this.isContextual(tt._as) ) { - const node: N.TsAsExpression = this.startNodeAt( + const node = this.startNodeAt( leftStartPos, leftStartLoc, ); @@ -2579,7 +2675,9 @@ export default (superClass: Class): Class => */ checkDuplicateExports() {} - parseImport(node: N.Node): N.AnyImport { + parseImport( + node: Undone, + ): N.AnyImport { node.importKind = "value"; if ( tokenIsIdentifier(this.state.type) || @@ -2603,18 +2701,22 @@ export default (superClass: Class): Class => } if (tokenIsIdentifier(this.state.type) && ahead.type === tt.eq) { - return this.tsParseImportEqualsDeclaration(node); + return this.tsParseImportEqualsDeclaration( + node as Undone, + ); } } - const importNode = super.parseImport(node); + const importNode = super.parseImport(node as Undone); /*:: invariant(importNode.type !== "TSImportEqualsDeclaration") */ // `import type` can only be used on imports with named imports or with a // default import - but not both if ( importNode.importKind === "type" && + // @ts-expect-error refine typings importNode.specifiers.length > 1 && + // @ts-expect-error refine typings importNode.specifiers[0].type === "ImportDefaultSpecifier" ) { this.raise(TSErrors.TypeImportCannotSpecifyDefaultAndNamed, { @@ -2625,7 +2727,7 @@ export default (superClass: Class): Class => return importNode; } - parseExport(node: N.Node): N.AnyExport { + parseExport(node: Undone): N.AnyExport { if (this.match(tt._import)) { // `export import A = B;` this.next(); // eat `tt._import` @@ -2638,16 +2740,19 @@ export default (superClass: Class): Class => } else { node.importKind = "value"; } - return this.tsParseImportEqualsDeclaration(node, /* isExport */ true); + return this.tsParseImportEqualsDeclaration( + node as Undone, + /* isExport */ true, + ); } else if (this.eat(tt.eq)) { // `export = x;` - const assign: N.TsExportAssignment = node; - assign.expression = this.parseExpression(); + const assign = node as Undone; + assign.expression = super.parseExpression(); this.semicolon(); return this.finishNode(assign, "TSExportAssignment"); } else if (this.eatContextual(tt._as)) { // `export as namespace A;` - const decl: N.TsNamespaceExportDeclaration = node; + const decl = node as Undone; // See `parseNamespaceExportDeclaration` in TypeScript's own parser this.expectContextual(tt._namespace); decl.id = this.parseIdentifier(); @@ -2664,7 +2769,9 @@ export default (superClass: Class): Class => node.exportKind = "value"; } - return super.parseExport(node); + return super.parseExport( + node as Undone, + ); } } @@ -2676,17 +2783,18 @@ export default (superClass: Class): Class => parseExportDefaultExpression(): N.Expression | N.Declaration { if (this.isAbstractClass()) { - const cls = this.startNode(); + const cls = this.startNode(); this.next(); // Skip "abstract" cls.abstract = true; - this.parseClass(cls, true, true); - return cls; + return this.parseClass(cls, true, true); } // export default interface allowed in: // https://github.com/Microsoft/TypeScript/pull/16040 if (this.match(tt._interface)) { - const result = this.tsParseInterfaceDeclaration(this.startNode()); + const result = this.tsParseInterfaceDeclaration( + this.startNode(), + ); if (result) return result; } @@ -2741,15 +2849,20 @@ export default (superClass: Class): Class => return declaration; } - parseStatementContent(context: ?string, topLevel: ?boolean): N.Statement { + parseStatementContent( + context?: string | null, + topLevel?: boolean | null, + ): N.Statement { if (this.match(tt._const) && this.isLookaheadContextual("enum")) { - const node: N.TsEnumDeclaration = this.startNode(); + const node = this.startNode(); this.expect(tt._const); // eat 'const' return this.tsParseEnumDeclaration(node, { const: true }); } if (this.isContextual(tt._enum)) { - return this.tsParseEnumDeclaration(this.startNode()); + return this.tsParseEnumDeclaration( + this.startNode(), + ); } if (this.isContextual(tt._interface)) { @@ -2760,11 +2873,11 @@ export default (superClass: Class): Class => return super.parseStatementContent(context, topLevel); } - parseAccessModifier(): ?N.Accessibility { + parseAccessModifier(): N.Accessibility | undefined | null { return this.tsParseModifier(["public", "protected", "private"]); } - tsHasSomeModifiers(member: any, modifiers: TsModifier[]): boolean { + tsHasSomeModifiers(member: any, modifiers: readonly TsModifier[]): boolean { return modifiers.some(modifier => { if (tsIsAccessModifier(modifier)) { return member.accessibility === modifier; @@ -2794,7 +2907,7 @@ export default (superClass: Class): Class => "abstract", "readonly", "static", - ]; + ] as const; this.tsParseModifiers({ modified: member, allowedModifiers: modifiers, @@ -2812,7 +2925,10 @@ export default (superClass: Class): Class => at: this.state.curPosition(), }); } - this.parseClassStaticBlock(classBody, ((member: any): N.StaticBlock)); + super.parseClassStaticBlock( + classBody, + member as any as N.StaticBlock, + ); } else { this.parseClassMemberWithIsStatic( classBody, @@ -2831,40 +2947,42 @@ export default (superClass: Class): Class => parseClassMemberWithIsStatic( classBody: N.ClassBody, - member: N.ClassMember | N.TsIndexSignature, + member: Undone, state: N.ParseClassMemberState, isStatic: boolean, ): void { - const idx = this.tsTryParseIndexSignature(member); + const idx = this.tsTryParseIndexSignature( + member as Undone, + ); if (idx) { classBody.body.push(idx); - if ((member: any).abstract) { + if ((member as any).abstract) { this.raise(TSErrors.IndexSignatureHasAbstract, { at: member }); } - if ((member: any).accessibility) { + if ((member as any).accessibility) { this.raise(TSErrors.IndexSignatureHasAccessibility, { at: member, - modifier: (member: any).accessibility, + modifier: (member as any).accessibility, }); } - if ((member: any).declare) { + if ((member as any).declare) { this.raise(TSErrors.IndexSignatureHasDeclare, { at: member }); } - if ((member: any).override) { + if ((member as any).override) { this.raise(TSErrors.IndexSignatureHasOverride, { at: member }); } return; } - if (!this.state.inAbstractClass && (member: any).abstract) { + if (!this.state.inAbstractClass && (member as any).abstract) { this.raise(TSErrors.NonAbstractClassHasAbstractMethod, { at: member, }); } - if ((member: any).override) { + if ((member as any).override) { if (!state.hadSuperClass) { this.raise(TSErrors.OverrideNotInSubClass, { at: member }); } @@ -2872,7 +2990,12 @@ export default (superClass: Class): Class => /*:: invariant(member.type !== "TSIndexSignature") */ - super.parseClassMemberWithIsStatic(classBody, member, state, isStatic); + super.parseClassMemberWithIsStatic( + classBody, + member as Undone, + state, + isStatic, + ); } parsePostMemberNameModifiers( @@ -2881,11 +3004,11 @@ export default (superClass: Class): Class => const optional = this.eat(tt.question); if (optional) methodOrProp.optional = true; - if ((methodOrProp: any).readonly && this.match(tt.parenL)) { + if ((methodOrProp as any).readonly && this.match(tt.parenL)) { this.raise(TSErrors.ClassMethodHasReadonly, { at: methodOrProp }); } - if ((methodOrProp: any).declare && this.match(tt.parenL)) { + if ((methodOrProp as any).declare && this.match(tt.parenL)) { this.raise(TSErrors.ClassMethodHasDeclare, { at: methodOrProp }); } } @@ -2894,13 +3017,15 @@ export default (superClass: Class): Class => // is that e.g. `type()` is valid JS, so we must try parsing that first. // If it's really a type, we will parse `type` as the statement, and can correct it here // by parsing the rest. + // @ts-expect-error plugin overrides interfaces parseExpressionStatement( - node: N.ExpressionStatement, + node: Undone, expr: N.Expression, ): N.Statement { const decl = expr.type === "Identifier" - ? this.tsParseExpressionStatement(node, expr) + ? // @ts-expect-error refine typings + this.tsParseExpressionStatement(node, expr) : undefined; return decl || super.parseExpressionStatement(node, expr); } @@ -2917,7 +3042,7 @@ export default (superClass: Class): Class => expr: N.Expression, startPos: number, startLoc: Position, - refExpressionErrors?: ?ExpressionErrors, + refExpressionErrors?: ExpressionErrors | null, ): N.Expression { // only do the expensive clone if there is a question mark // and if we come from inside parens @@ -2963,7 +3088,7 @@ export default (superClass: Class): Class => } if (this.match(tt.colon)) { - const typeCastNode: N.TsTypeCastExpression = this.startNodeAt( + const typeCastNode = this.startNodeAt( startPos, startLoc, ); @@ -2976,7 +3101,9 @@ export default (superClass: Class): Class => return node; } - parseExportDeclaration(node: N.ExportNamedDeclaration): ?N.Declaration { + parseExportDeclaration( + node: N.ExportNamedDeclaration, + ): N.Declaration | undefined | null { if (!this.state.isAmbientContext && this.isContextual(tt._declare)) { return this.tsInAmbientContext(() => this.parseExportDeclaration(node)); } @@ -2997,7 +3124,7 @@ export default (superClass: Class): Class => } const isIdentifier = tokenIsIdentifier(this.state.type); - const declaration: ?N.Declaration = + const declaration: N.Declaration | undefined | null = (isIdentifier && this.tsTryParseExportDeclaration()) || super.parseExportDeclaration(node); @@ -3024,7 +3151,7 @@ export default (superClass: Class): Class => parseClassId( node: N.Class, isStatement: boolean, - optionalId: ?boolean, + optionalId?: boolean | null, ): void { if ((!isStatement || optionalId) && this.isContextual(tt._implements)) { return; @@ -3034,7 +3161,7 @@ export default (superClass: Class): Class => node, isStatement, optionalId, - (node: any).declare ? BIND_TS_AMBIENT : BIND_CLASS, + (node as any).declare ? BIND_TS_AMBIENT : BIND_CLASS, ); const typeParameters = this.tsTryParseTypeParameters( this.tsParseInOutModifiers.bind(this), @@ -3078,15 +3205,16 @@ export default (superClass: Class): Class => parseClassPrivateProperty( node: N.ClassPrivateProperty, ): N.ClassPrivateProperty { - // $FlowIgnore + // @ts-expect-error if (node.abstract) { this.raise(TSErrors.PrivateElementHasAbstract, { at: node }); } - // $FlowIgnore + // @ts-expect-error if (node.accessibility) { this.raise(TSErrors.PrivateElementHasAccessibility, { at: node, + // @ts-expect-error refine typings modifier: node.accessibility, }); } @@ -3110,7 +3238,7 @@ export default (superClass: Class): Class => }); } - // $FlowIgnore + // @ts-expect-error const { declare = false, kind } = method; if (declare && (kind === "get" || kind === "set")) { @@ -3153,6 +3281,7 @@ export default (superClass: Class): Class => super.parseClassSuper(node); // handle `extends f< if (node.superClass && (this.match(tt.lt) || this.match(tt.bitShiftL))) { + // @ts-expect-error refine typings node.superTypeParameters = this.tsParseTypeArgumentsInExpression(); } if (this.eatContextual(tt._implements)) { @@ -3160,11 +3289,22 @@ export default (superClass: Class): Class => } } - parseObjPropValue(prop: N.ObjectMember, ...args): void { + parseObjPropValue( + prop: Undone, + ...args: [ + number | undefined | null, + Position | undefined | null, + boolean, + boolean, + boolean, + boolean, + ExpressionErrors | null, + ] + ): N.ObjectMethod | N.ObjectProperty { const typeParameters = this.tsTryParseTypeParameters(); if (typeParameters) prop.typeParameters = typeParameters; - super.parseObjPropValue(prop, ...args); + return super.parseObjPropValue(prop, ...args); } parseFunctionParams(node: N.Function, allowModifiers?: boolean): void { @@ -3205,10 +3345,12 @@ export default (superClass: Class): Class => return super.parseAsyncArrowFromCallExpression(node, call); } - parseMaybeAssign(...args): N.Expression { + parseMaybeAssign( + ...args: [ExpressionErrors | null, Function] + ): N.Expression { // Note: When the JSX plugin is on, type assertions (` x`) aren't valid syntax. - let state: ?State; + let state: State | undefined | null; let jsx; let typeCast; @@ -3246,7 +3388,7 @@ export default (superClass: Class): Class => // so we still need to clone it. if (!state || state === this.state) state = this.state.clone(); - let typeParameters: ?N.TsTypeParameterDeclaration; + let typeParameters: N.TsTypeParameterDeclaration | undefined | null; const arrow = this.tryParse(abort => { // This is similar to TypeScript's `tryParseParenthesizedArrowFunctionExpression`. typeParameters = this.tsParseTypeParameters(); @@ -3293,6 +3435,7 @@ export default (superClass: Class): Class => // in case of (x) => 2, we don't consider (x) as a type assertion // because of this error. if (typeParameters) this.reportReservedArrowTypeParam(typeParameters); + // @ts-expect-error refine typings return arrow.node; } @@ -3320,6 +3463,7 @@ export default (superClass: Class): Class => /*:: invariant(arrow.failState) */ this.state = arrow.failState; if (typeParameters) this.reportReservedArrowTypeParam(typeParameters); + // @ts-expect-error refine typings return arrow.node; } @@ -3347,7 +3491,9 @@ export default (superClass: Class): Class => } // Handle type assertions - parseMaybeUnary(refExpressionErrors?: ?ExpressionErrors): N.Expression { + parseMaybeUnary( + refExpressionErrors?: ExpressionErrors | null, + ): N.Expression { if (!this.hasPlugin("jsx") && this.match(tt.lt)) { return this.tsParseTypeAssertion(); } else { @@ -3355,7 +3501,9 @@ export default (superClass: Class): Class => } } - parseArrow(node: N.ArrowFunctionExpression): ?N.ArrowFunctionExpression { + parseArrow( + node: Undone, + ): Undone | undefined | null { if (this.match(tt.colon)) { // This is different from how the TS parser does it. // TS uses lookahead. The Babel Parser parses it as a parenthesized expression and converts. @@ -3372,6 +3520,7 @@ export default (superClass: Class): Class => if (!result.thrown) { if (result.error) this.state = result.failState; + // @ts-expect-error refine typings node.returnType = result.node; } } @@ -3390,7 +3539,7 @@ export default (superClass: Class): Class => this.raise(TSErrors.PatternIsOptional, { at: param }); } - ((param: any): N.Identifier).optional = true; + (param as any as N.Identifier).optional = true; } const type = this.tsTryParseTypeAnnotation(); if (type) param.typeAnnotation = type; @@ -3463,8 +3612,14 @@ export default (superClass: Class): Class => } } + // @ts-expect-error plugin overrides interfaces isValidLVal( - type: string, + type: + | "TSTypeCastExpression" + | "TSParameterProperty" + | "TSNonNullExpression" + | "TSAsExpression" + | "TSTypeAssertion", isUnparenthesizedInAssign: boolean, binding: BindingTypes, ) { @@ -3514,7 +3669,9 @@ export default (superClass: Class): Class => return super.parseMaybeDecoratorArguments(expr); } - checkCommaAfterRest(close): boolean { + checkCommaAfterRest( + close: typeof charCodes[keyof typeof charCodes], + ): boolean { if ( this.state.isAmbientContext && this.match(tt.comma) && @@ -3542,7 +3699,13 @@ export default (superClass: Class): Class => ); } - parseMaybeDefault(...args): N.Pattern { + parseMaybeDefault( + ...args: [ + startPos?: number | null, + startLoc?: Position | null, + left?: N.Pattern | null, + ] + ): N.Pattern { const node = super.parseMaybeDefault(...args); if ( @@ -3593,14 +3756,19 @@ export default (superClass: Class): Class => return type; } - toAssignableList(exprList: N.Expression[]): void { + toAssignableList( + exprList: N.Expression[], + ...args: [Position | undefined | null, boolean] + ): void { for (let i = 0; i < exprList.length; i++) { const expr = exprList[i]; if (expr?.type === "TSTypeCastExpression") { - exprList[i] = this.typeCastToParameter(expr); + exprList[i] = this.typeCastToParameter( + expr as N.TsTypeCastExpression, + ); } } - super.toAssignableList(...arguments); + super.toAssignableList(exprList, ...args); } typeCastToParameter(node: N.TsTypeCastExpression): N.Node { @@ -3633,10 +3801,13 @@ export default (superClass: Class): Class => // handles `` if (this.match(tt.lt) || this.match(tt.bitShiftL)) { const typeArguments = this.tsTryParseAndCatch(() => + // @ts-expect-error: refine typings this.tsParseTypeArgumentsInExpression(), ); + // @ts-expect-error: refine typings if (typeArguments) node.typeParameters = typeArguments; } + // @ts-expect-error calling JSX methods return super.jsxParseOpeningElementAfterName(node); } @@ -3673,9 +3844,12 @@ export default (superClass: Class): Class => } } - parseClass(node: T, ...args: any[]): T { + parseClass( + node: Undone, + ...args: [boolean, boolean] + ): T { const oldInAbstractClass = this.state.inAbstractClass; - this.state.inAbstractClass = !!(node: any).abstract; + this.state.inAbstractClass = !!(node as any).abstract; try { return super.parseClass(node, ...args); } finally { @@ -3685,11 +3859,11 @@ export default (superClass: Class): Class => tsParseAbstractDeclaration( node: any, - ): N.ClassDeclaration | ?N.TsInterfaceDeclaration { + ): N.ClassDeclaration | N.TsInterfaceDeclaration | undefined | null { if (this.match(tt._class)) { node.abstract = true; return this.parseClass( - (node: N.ClassDeclaration), + node as N.ClassDeclaration, /* isStatement */ true, /* optionalId */ false, ); @@ -3705,7 +3879,7 @@ export default (superClass: Class): Class => at: node, }); return this.tsParseInterfaceDeclaration( - (node: N.TsInterfaceDeclaration), + node as N.TsInterfaceDeclaration, ); } } else { @@ -3713,11 +3887,25 @@ export default (superClass: Class): Class => } } - parseMethod(...args: any[]) { + parseMethod< + T extends N.ObjectMethod | N.ClassMethod | N.ClassPrivateMethod, + >( + ...args: [ + Undone, + boolean, + boolean, + boolean, + boolean, + T["type"], + boolean, + ] + ) { const method = super.parseMethod(...args); + // @ts-expect-error abstract is not in ObjectMethod if (method.abstract) { const hasBody = this.hasPlugin("estree") - ? !!method.value.body + ? // @ts-expect-error estree typings + !!method.value.body : !!method.body; if (hasBody) { const { key } = method; diff --git a/packages/babel-parser/src/plugins/typescript/scope.js b/packages/babel-parser/src/plugins/typescript/scope.ts similarity index 96% rename from packages/babel-parser/src/plugins/typescript/scope.js rename to packages/babel-parser/src/plugins/typescript/scope.ts index 0019ac9cdf1c..113b410ac402 100644 --- a/packages/babel-parser/src/plugins/typescript/scope.js +++ b/packages/babel-parser/src/plugins/typescript/scope.ts @@ -1,5 +1,3 @@ -// @flow - import { Position } from "../../util/location"; import ScopeHandler, { Scope } from "../../util/scope"; import { @@ -49,7 +47,7 @@ export default class TypeScriptScopeHandler extends ScopeHandler): Class => +export default (superClass: { + new (...args: any): Parser; +}): { + new (...args: any): Parser; +} => class extends superClass { - parseV8Intrinsic(): N.Expression { + parseV8Intrinsic(): N.Expression | void { if (this.match(tt.modulo)) { const v8IntrinsicStartLoc = this.state.startLoc; // let the `loc` of Identifier starts from `%` - const node = this.startNode(); + const node = this.startNode(); this.next(); // eat '%' if (tokenIsIdentifier(this.state.type)) { const name = this.parseIdentifierName(this.state.start); const identifier = this.createIdentifier(node, name); + // @ts-expect-error: avoid mutating AST types identifier.type = "V8IntrinsicIdentifier"; if (this.match(tt.parenL)) { return identifier; diff --git a/packages/babel-parser/src/tokenizer/context.js b/packages/babel-parser/src/tokenizer/context.ts similarity index 94% rename from packages/babel-parser/src/tokenizer/context.js rename to packages/babel-parser/src/tokenizer/context.ts index f0b0c687a98a..4bb309c6a735 100644 --- a/packages/babel-parser/src/tokenizer/context.js +++ b/packages/babel-parser/src/tokenizer/context.ts @@ -1,5 +1,3 @@ -// @flow - // The token context is used in JSX plugin to track // jsx tag / jsx text / normal JavaScript expression @@ -14,7 +12,7 @@ export class TokContext { } const types: { - [key: string]: TokContext, + [key: string]: TokContext; } = { brace: new TokContext("{"), // normal JavaScript expression j_oTag: new TokContext("` line comment const comment = this.skipLineComment(3); if (comment !== undefined) { + // @ts-expect-error strictNullCheck is not enabled this.addComment(comment); if (this.options.attachComment) comments.push(comment); } @@ -408,6 +411,7 @@ export default class Tokenizer extends CommentsParser { // `