diff --git a/package.json b/package.json index dc67342d8..524757eec 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "jsdom": "^19.0.0", "prettier": "^2.1.2", "ts-jest": "^29.0.0", - "typescript": "^4.0.3" + "typescript": "^5.4.5" }, "jest": { "snapshotFormat": { diff --git a/src/infiniteLoops/__tests__/instrument.ts b/src/infiniteLoops/__tests__/instrument.ts index 56886b86a..8244c07b7 100644 --- a/src/infiniteLoops/__tests__/instrument.ts +++ b/src/infiniteLoops/__tests__/instrument.ts @@ -12,7 +12,7 @@ import { function mockFunctionsAndState() { const theState = undefined - const functions = {} + const functions: Partial> = {} const returnFirst = (...args: any[]) => args[0] const nothing = (..._args: any[]) => {} functions[functionNames.nothingFunction] = nothing diff --git a/src/infiniteLoops/instrument.ts b/src/infiniteLoops/instrument.ts index acd6c7354..7b5846f2d 100644 --- a/src/infiniteLoops/instrument.ts +++ b/src/infiniteLoops/instrument.ts @@ -4,8 +4,9 @@ import type es from 'estree' import { transformImportDeclarations } from '../transpiler/transpiler' import type { Node } from '../types' import * as create from '../utils/ast/astCreator' -import { recursive, simple, WalkerCallback } from '../utils/walkers' import { getIdsFromDeclaration } from '../utils/ast/helpers' +import { objectValues } from '../utils/misc' +import { recursive, simple, WalkerCallback } from '../utils/walkers' // transforms AST of program const globalIds = { @@ -40,8 +41,8 @@ enum FunctionNames { * E.g. "function f(f)..." -> "function f_0(f_1)..." * @param predefined A table of [key: string, value:string], where variables named 'key' will be renamed to 'value' */ -function unshadowVariables(program: Node, predefined = {}) { - for (const name of Object.values(globalIds)) { +function unshadowVariables(program: Node, predefined: Record = {}) { + for (const name of objectValues(globalIds)) { predefined[name] = name } const seenIds = new Set() @@ -607,7 +608,7 @@ function instrument( builtins: Iterable ): string { const { builtinsId, functionsId, stateId } = globalIds - const predefined = {} + const predefined: Record = {} predefined[builtinsId] = builtinsId predefined[functionsId] = functionsId predefined[stateId] = stateId @@ -640,7 +641,7 @@ function instrument( } export { - instrument, FunctionNames as InfiniteLoopRuntimeFunctions, - globalIds as InfiniteLoopRuntimeObjectNames + globalIds as InfiniteLoopRuntimeObjectNames, + instrument } diff --git a/src/interpreter/interpreter.ts b/src/interpreter/interpreter.ts index 2af008d68..97aa9f525 100644 --- a/src/interpreter/interpreter.ts +++ b/src/interpreter/interpreter.ts @@ -10,12 +10,12 @@ import * as errors from '../errors/errors' import { RuntimeSourceError } from '../errors/runtimeSourceError' import { checkEditorBreakpoints } from '../stdlib/inspector' import { + Variant, type Context, type ContiguousArrayElements, type Environment, type Node, - type Value, - Variant + type Value } from '../types' import * as create from '../utils/ast/astCreator' import { conditionalExpression, literal, primitive } from '../utils/ast/astCreator' @@ -637,7 +637,7 @@ export const evaluators: { [nodeType: string]: Evaluator } = { }, ObjectExpression: function*(node: es.ObjectExpression, context: Context) { - const obj = {} + const obj: Record = {} for (const propUntyped of node.properties) { // node.properties: es.Property | es.SpreadExpression, but // our Acorn is set to ES6 which cannot have a es.SpreadExpression diff --git a/src/parser/__tests__/disallowed-syntax.ts b/src/parser/__tests__/disallowed-syntax.ts index 5f6487027..6acf224f4 100644 --- a/src/parser/__tests__/disallowed-syntax.ts +++ b/src/parser/__tests__/disallowed-syntax.ts @@ -412,7 +412,7 @@ test('Cannot use function expressions', () => { stripIndent` (function fib(x) { return x <= 1 ? x : fib(x-1) + fib(x-2); })(4); `, - { chapter: 5 } + { chapter: 5 as Chapter } ).toMatchInlineSnapshot(`"Line 1: Function expressions are not allowed"`) }) @@ -422,7 +422,7 @@ test('Cannot use function expressions - verbose', () => { "enable verbose"; (function fib(x) { return x <= 1 ? x : fib(x-1) + fib(x-2); })(4); `, - { chapter: 5 } + { chapter: 5 as Chapter } ).toMatchInlineSnapshot(` "Line 2, Column 1: Function expressions are not allowed You are trying to use Function expressions, which is not allowed (yet). @@ -435,7 +435,7 @@ test('Cannot use function expressions', () => { stripIndent` (function(x) { return x + 1; })(4); `, - { chapter: 5 } + { chapter: 5 as Chapter } ).toMatchInlineSnapshot(`"Line 1: Function expressions are not allowed"`) }) @@ -445,7 +445,7 @@ test('Cannot use function expressions - verbose', () => { "enable verbose"; (function(x) { return x + 1; })(4); `, - { chapter: 5 } + { chapter: 5 as Chapter } ).toMatchInlineSnapshot(` "Line 2, Column 1: Function expressions are not allowed You are trying to use Function expressions, which is not allowed (yet). @@ -840,7 +840,7 @@ test('no classes', () => { class Box { } `, - { chapter: 5 } + { chapter: 5 as Chapter } ).toMatchInlineSnapshot(` "Line 1: Class bodys are not allowed Line 1: Class declarations are not allowed" @@ -854,7 +854,7 @@ test('no classes - verbose', () => { class Box { } `, - { chapter: 5 } + { chapter: 5 as Chapter } ).toMatchInlineSnapshot(` "Line 2, Column 10: Class bodys are not allowed You are trying to use Class bodys, which is not allowed (yet). @@ -874,7 +874,7 @@ test('no super', () => { } } `, - { chapter: 5 } + { chapter: 5 as Chapter } ).toMatchInlineSnapshot(` "Line 3: Supers are not allowed Line 2: Function expressions are not allowed @@ -894,7 +894,7 @@ test('no super - verbose', () => { } } `, - { chapter: 5 } + { chapter: 5 as Chapter } ).toMatchInlineSnapshot(` "Line 4, Column 4: Supers are not allowed You are trying to use Supers, which is not allowed (yet). diff --git a/src/stepper/converter.ts b/src/stepper/converter.ts index f0f508802..927f3f7e7 100644 --- a/src/stepper/converter.ts +++ b/src/stepper/converter.ts @@ -53,7 +53,7 @@ export function nodeToValue(node: substituterNodes): any { return node.type === 'Literal' ? node.value : util.isBuiltinFunction(node) - ? builtin[(node as es.Identifier).name] + ? builtin[(node as es.Identifier).name as keyof typeof builtin] : // tslint:disable-next-line eval(javascriptify(node)) } @@ -62,7 +62,7 @@ export function nodeToValueWithContext(node: substituterNodes, context: Context) return node.type === 'Literal' ? node.value : util.isBuiltinFunction(node) - ? builtin[(node as es.Identifier).name] + ? builtin[(node as es.Identifier).name as keyof typeof builtin] : node.type === 'Identifier' && util.isImportedFunction(node, context) ? context.runtime.environments[0].head[node.name] : // tslint:disable-next-line @@ -77,11 +77,13 @@ function evaluateFunctionObject(node: substituterNodes, context: Context) { } visited.add(node) if (node.type === 'Identifier' && builtinFunctions[node.name]) { + // @ts-expect-error implicitAnyIndexError global[node.name] = builtinFunctions[node.name] } - for (const key in node) { + for (const k in node) { + const key = k as keyof typeof node if (node[key] && typeof node[key] === 'object') { - lookUpIdentifiers(node[key], visited) + lookUpIdentifiers(node[key] as any, visited) } } } diff --git a/src/stepper/stepper.ts b/src/stepper/stepper.ts index 23be27b58..8f95d63bb 100644 --- a/src/stepper/stepper.ts +++ b/src/stepper/stepper.ts @@ -22,6 +22,7 @@ import { dummyVariableDeclarator } from '../utils/ast/dummyAstCreator' import { filterImportDeclarations } from '../utils/ast/helpers' +import { objectGetOwnPropertyNames } from '../utils/misc' import { evaluateBinaryExpression, evaluateUnaryExpression } from '../utils/operators' import * as rttc from '../utils/rttc' import { checkProgramForUndefinedVariables } from '../validator/validator' @@ -210,7 +211,7 @@ function findMain( const freeNames: any[] = [] - const finders = { + const finders: Partial void>> = { Identifier(target: es.Identifier): void { seenBefore.set(target, target) let bound = false @@ -418,7 +419,7 @@ function substituteMain( * and push the appropriate access string into the path * 3. Return the dummyReplacement */ - const substituters = { + const substituters: Partial> = { // if name to be replaced is found, // push endMarker into path Identifier( @@ -1325,7 +1326,7 @@ function reduceMain( // converts body of code to string function bodify(target: substituterNodes): string { - const bodifiers = { + const bodifiers: Partial string>> = { Literal: (target: es.Literal): string => target.raw !== undefined ? target.raw : String(target.value), @@ -1409,7 +1410,7 @@ function reduceMain( // generates string to explain current step function explain(target: substituterNodes): string { - const explainers = { + const explainers: Partial string>> = { BinaryExpression: (target: es.BinaryExpression): string => 'Binary expression ' + bodify(target) + ' evaluated', @@ -1496,7 +1497,7 @@ function reduceMain( return explainer === undefined ? '...' : explainer(target) } - const reducers = { + const reducers: Partial> = { // source 0 Identifier( node: es.Identifier, @@ -1738,8 +1739,10 @@ function reduceMain( paths, explain(node) ] + // @ts-expect-error implicitAnyIndexError } else if (typeof builtin[(callee as es.Identifier).name] === 'function') { // Source specific built-in function + // @ts-expect-error implicitAnyIndexError return [builtin[(callee as es.Identifier).name](...args), context, paths, explain(node)] } else { // Common built-in function @@ -1825,10 +1828,12 @@ function reduceMain( ) // Fix path highlighting after preserving first statement - path.forEach(pathStep => { + path.forEach((pathStep: any[]) => { pathStep.forEach((_, i) => { if (i == 0) { - pathStep[i] = pathStep[i].replace(/\d+/g, match => String(Number(match) + 1)) + pathStep[i] = pathStep[i].replace(/\d+/g, (match: any) => + String(Number(match) + 1) + ) } }) }) @@ -2050,10 +2055,12 @@ function reduceMain( ) // Fix path highlighting after preserving first statement - path.forEach(pathStep => { + path.forEach((pathStep: any[]) => { pathStep.forEach((_, i) => { if (i == 0) { - pathStep[i] = pathStep[i].replace(/\d+/g, match => String(Number(match) + 1)) + pathStep[i] = pathStep[i].replace(/\d+/g, (match: any) => + String(Number(match) + 1) + ) } }) }) @@ -2269,10 +2276,12 @@ function reduceMain( ) // Fix path highlighting after preserving first statement - path.forEach(pathStep => { + path.forEach((pathStep: any[]) => { pathStep.forEach((_, i) => { if (i == 0) { - pathStep[i] = pathStep[i].replace(/\d+/g, match => String(Number(match) + 1)) + pathStep[i] = pathStep[i].replace(/\d+/g, (match: any) => + String(Number(match) + 1) + ) } }) }) @@ -2483,7 +2492,7 @@ function treeifyMain(target: substituterNodes): substituterNodes { // has an identifier: replace with the name // else: replace with an identifer "=>" let verboseCount = 0 - const treeifiers = { + const treeifiers: Partial any>> = { // Identifier: return ExpressionStatement: (target: es.ExpressionStatement): es.ExpressionStatement => { return ast.expressionStatement(treeify(target.expression) as es.Expression) @@ -2635,7 +2644,7 @@ function jsTreeifyMain( // visited before recursing to this target: replace with the name // else: replace with a FunctionExpression let verboseCount = 0 - const treeifiers = { + const treeifiers: Partial substituterNodes>> = { Identifier: (target: es.Identifier): es.Identifier => { if (readOnly && target.name.startsWith('anonymous_')) { return ast.identifier('[Function]') @@ -2817,7 +2826,7 @@ function pathifyMain( let endIndex = path === undefined ? 0 : path.length - 1 const redexMarker = ast.identifier('@redex') as substituterNodes const withBrackets = ast.identifier('(@redex)') as substituterNodes - const pathifiers = { + const pathifiers: Partial> = { ExpressionStatement: (target: es.ExpressionStatement): es.ExpressionStatement => { let exp = jsTreeifyMain(target.expression, visited, true) as es.Expression if (path[pathIndex] === 'expression') { @@ -3262,9 +3271,9 @@ function substPredefinedFns(program: es.Program, context: Context): [es.Program, function substPredefinedConstants(program: es.Program): es.Program { const constants = [['undefined', undefined]] - const mathConstants = Object.getOwnPropertyNames(Math) + const mathConstants = objectGetOwnPropertyNames(Math) .filter(name => typeof Math[name] !== 'function') - .map(name => ['math_' + name, Math[name]]) + .map(name => ['math_' + (name as string), Math[name]]) as (string | undefined)[][] let substed = program for (const nameValuePair of constants.concat(mathConstants)) { substed = substituteMain( diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 57d362366..fa06fe3e8 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -42,3 +42,12 @@ export function objectKeys(obj: Record(obj: Record) { return Object.values(obj) as T[] } + +/** + * Type safe `Object.getOwnPropertyNames` + */ +export function objectGetOwnPropertyNames( + obj: Record +): T[] { + return Object.getOwnPropertyNames(obj) as T[] +} diff --git a/src/utils/uniqueIds.ts b/src/utils/uniqueIds.ts index 348dfa149..4239d2a3b 100644 --- a/src/utils/uniqueIds.ts +++ b/src/utils/uniqueIds.ts @@ -20,7 +20,7 @@ const globalIdNames = [ export type NativeIds = Record<(typeof globalIdNames)[number], es.Identifier> export function getNativeIds(program: es.Program, usedIdentifiers: Set): NativeIds { - const globalIds = {} + const globalIds: Partial = {} for (const identifier of globalIdNames) { globalIds[identifier] = create.identifier(getUniqueId(usedIdentifiers, identifier)) } diff --git a/src/vm/svml-compiler.ts b/src/vm/svml-compiler.ts index 0710ab4eb..af3d8a7b5 100644 --- a/src/vm/svml-compiler.ts +++ b/src/vm/svml-compiler.ts @@ -5,9 +5,9 @@ import { ConstAssignment, UndefinedVariable } from '../errors/errors' import { parse } from '../parser/parser' import { CONSTANT_PRIMITIVES, - generatePrimitiveFunctionCode, INTERNAL_FUNCTIONS, PRIMITIVE_FUNCTION_NAMES, + generatePrimitiveFunctionCode, vmPrelude } from '../stdlib/vm.prelude' import type { Context, ContiguousArrayElements, Node } from '../types' @@ -485,7 +485,17 @@ function compileStatements( } // each compiler should return a maxStackSize -const compilers = { +const compilers: Partial< + Record< + Node['type'], + ( + node: Node, + indexTable: Map[], + insertFlag: boolean, + isTailCallPosition?: boolean + ) => ReturnType + > +> = { // wrapper Program(node: Node, indexTable: Map[], insertFlag: boolean) { node = node as es.Program diff --git a/src/vm/util.ts b/src/vm/util.ts index 7cf9e8d23..bbd3f2f38 100644 --- a/src/vm/util.ts +++ b/src/vm/util.ts @@ -29,8 +29,6 @@ const OPCODES_STR = { [OpCodes.DIVF]: 'DIVF ', [OpCodes.MODG]: 'MODG ', [OpCodes.MODF]: 'MODF ', - [OpCodes.NEGG]: 'NEGG ', - [OpCodes.NEGF]: 'NEGF ', [OpCodes.NOTG]: 'NOTG ', [OpCodes.NOTB]: 'NOTB ', [OpCodes.LTG]: 'LTG ', @@ -44,9 +42,6 @@ const OPCODES_STR = { [OpCodes.EQG]: 'EQG ', [OpCodes.EQF]: 'EQF ', [OpCodes.EQB]: 'EQB ', - [OpCodes.NEQG]: 'NEQG ', - [OpCodes.NEQF]: 'NEQF ', - [OpCodes.NEQB]: 'NEQB ', [OpCodes.NEWC]: 'NEWC ', [OpCodes.NEWA]: 'NEWA ', [OpCodes.LDLG]: 'LDLG ', @@ -156,7 +151,7 @@ const OPCODES_STR = { // get name of opcode for debugging export function getName(op: number) { - return OPCODES_STR[op] // need to add guard in case op does not exist + return OPCODES_STR[op as keyof typeof OPCODES_STR] // need to add guard in case op does not exist } // pretty-print the program diff --git a/tsconfig.json b/tsconfig.json index 7ee1d8ed8..3dc7bb519 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,6 @@ "noImplicitThis": true, "noImplicitAny": true, "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, "resolveJsonModule": true, "noUnusedLocals": true, "incremental": true diff --git a/yarn.lock b/yarn.lock index 4971dfb12..9ba4a2515 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5812,10 +5812,10 @@ typed-array-length@^1.0.5: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" -typescript@^4.0.3: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@^5.4.5: + version "5.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6"