DEV Community

Cover image for TypeScript 7 Migration: Lambda and CDK Projects
Sascha Mirow-Stanitzok
Sascha Mirow-Stanitzok

Posted on Originally published at sascha-ms.de

TypeScript 7 Migration: Lambda and CDK Projects

A TypeScript 7 migration sounds like bumping a version number in package.json, and that is exactly how I treated it at first. Then the first build stops with three errors in tsconfig.json, cdk synth fails with a cryptic message from ts-node, and npm refuses to install typescript-eslint. The compiler itself is faster than ever. It’s the tools around it that slow you down.

This matters to me because Lambda and CDK projects written in TypeScript often run on an old tsconfig for years. So in this article you walk through the whole upgrade once: from checking the configuration to replacing ts-node and keeping ESLint on TypeScript 6 for the time being. Every error message below comes from a real test run with TypeScript 7.0.2.

In short

TypeScript 7 is the new compiler written in Go, and it turns every option that TypeScript 6 deprecated into a hard error. In Lambda and CDK projects that is quick to fix, because esbuild does the building anyway and tsc only checks types. The biggest pitfall is that TypeScript 7.0 ships without a compiler API: ts-node, ts-jest and typescript-eslint need TypeScript 6 alongside it for now.

What you will learn in this article

  • Which tsconfig options TypeScript 7 rejects and what replaces them
  • How a small check script finds the pitfalls before you switch
  • Why ts-node breaks the CDK app and how tsx replaces it
  • How to run TypeScript 6 and 7 side by side for ESLint
  • How to verify that bundle and deployment still work afterwards

What is different in TypeScript 7?

TypeScript 7 is the compiler the TypeScript team has ported to Go over the last few years. Version 7.0 has been stable since July 2026, and the current release is 7.0.2. You still install it from the npm package typescript, and the command is still tsc. If you used the preview package @typescript/native-preview before, you can remove it again.

The obvious gain is speed. In the official announcement, the build time of the VS Code code base drops from about 126 to just under 11 seconds, and for other large projects the factor sits between almost 8 and 9. Native code and parallel type checking make this possible: the new --checkers flag sets how many checker workers run at once (default 4), and --singleThreaded turns that off.

Deprecated options are now errors

For the migration, something else matters more. In March 2026, TypeScript 6.0 deprecated a whole set of options but still accepted them with "ignoreDeprecations": "6.0". TypeScript 7 removes that escape hatch. What used to be a warning in 6.0 now stops the build. At the same time, the new defaults from 6.0 still apply, for example strict: true, types: [] and a rootDir that points to the folder containing tsconfig.json.

Option Status in TypeScript 7 Replacement
moduleResolution: "node" / "node10" removed "bundler" or "nodenext"
baseUrl removed start entries in paths with ./
target: "es5" removed at least ES2015, ES2023 for Lambda
module: "amd", "umd", "system" removed "preserve", "esnext", "nodenext"
esModuleInterop: false no longer allowed delete the line
downlevelIteration, outFile removed delete the line, use a bundler
types not set default [] "types": ["node"]
rootDir not set tsconfig folder "rootDir": "./src"

No compiler API in 7.0

The second big change affects tools that don’t call TypeScript as a command but load it as a library. This so-called compiler API (the programming interface other programs use to parse code and query types) does not exist in 7.0. A new, different API is planned for 7.1, which I already tried out in the article on the TypeScript 7.1 beta. Until it is stable, the TypeScript team ships a compatibility package: @typescript/typescript6 provides the tsc6 command and the old API.

Why Lambda and CDK projects are especially affected

In a typical Serverless project with TypeScript, more tools depend on TypeScript than you notice day to day. The good news: you don’t need the TypeScript compiler to build the Lambda functions at all. NodejsFunction in the AWS CDK and Serverless Framework v4 bundle with esbuild, and esbuild simply strips the types without checking them. Here, tsc is only the type checker.

That makes the tools that need the compiler API the critical ones. Older projects generated with cdk init start the CDK app with ts-node and test with ts-jest. Both load TypeScript as a library. By the way, the current CDK templates have already switched: they use tsx for the app, @swc/jest for tests and TypeScript ~7.0.2.

Tool Needs the compiler API? What you need to do
tsc no, it is the compiler switch to 7, fix tsconfig
esbuild (CDK NodejsFunction, Serverless Framework v4) no nothing
tsx no (uses esbuild) nothing, replaces ts-node
Vitest not for running tests nothing
ts-node yes replace with tsx
ts-jest yes, peer dependency typescript <7 Vitest or @swc/jest, otherwise TypeScript 6
typescript-eslint 8.72 yes, peer dependency <6.1.0 install TypeScript 6 alongside

🎬 Which TypeScript does each tool need after the upgrade? – this animated diagram only runs on my site: watch it on sascha-ms.de

TypeScript 7 migration step by step

The example is a small, made-up bike rental: a Lambda function behind an HTTP API calculates the rental price, a shared module lives in src/shared and is imported through the @shared/* alias. The infrastructure is defined in the AWS CDK, and a serverless.yml shows the same function with the Serverless Framework.

Prerequisites

  • Node.js 22 or newer locally (tested with Node.js 24)
  • a project on TypeScript 5.x or 6.x that currently builds cleanly
  • Git, so you can commit each step separately and roll back if needed
  • for the deployment at the end: AWS credentials and a bootstrapped CDK account (optional)

Step 1: Create a branch and measure the baseline

Before you change anything, write down how long type checking takes today and whether it passes. Otherwise you won’t know afterwards whether an error is new or was already there.

git switch -c chore/typescript-7
npx tsc --version
time npx tsc --noEmit
Enter fullscreen mode Exit fullscreen mode

Step 2: Check the tsconfig automatically

Instead of ticking off the table above by hand, let a small script read tsconfig.json. It has no dependencies, understands comments and trailing commas (the tsconfig is JSONC, meaning JSON with comments) and reports every option that will cause trouble under TypeScript 7. One caveat: the script only checks the file itself. It doesn’t follow an extends to a base configuration, so check that one separately.

// scripts/ts7-check.ts
import { readFileSync } from 'node:fs';

export interface Finding {
  option: string;
  problem: string;
  fix: string;
}

type Options = Record<string, unknown>;

/** Removes comments and trailing commas before closing brackets (tsconfig is JSONC). */
export function parseJsonc(text: string): unknown {
  let out = '';
  let inString = false;
  for (let i = 0; i < text.length; i++) {
    const ch = text[i];
    const next = text[i + 1];
    if (inString) {
      out += ch;
      if (ch === '\\') {
        out += next ?? '';
        i++;
      } else if (ch === '"') {
        inString = false;
      }
      continue;
    }
    if (ch === '"') {
      inString = true;
      out += ch;
    } else if (ch === '/' && next === '/') {
      while (i < text.length && text[i] !== '\n') i++;
      out += '\n';
    } else if (ch === '/' && next === '*') {
      i += 2;
      while (i < text.length && !(text[i] === '*' && text[i + 1] === '/')) i++;
      i++;
    } else {
      out += ch;
    }
  }
  return JSON.parse(out.replace(/,(\s*[}\]])/g, '$1'));
}

const lower = (v: unknown) => (typeof v === 'string' ? v.toLowerCase() : v);

export function checkCompilerOptions(opts: Options, sourceDirs: string[] = []): Finding[] {
  const findings: Finding[] = [];
  const add = (option: string, problem: string, fix: string) => findings.push({ option, problem, fix });

  const resolution = lower(opts.moduleResolution);
  if (resolution === 'node' || resolution === 'node10' || resolution === 'classic') {
    add('moduleResolution', `"${String(opts.moduleResolution)}" no longer exists`, '"bundler" (with esbuild) or "nodenext" (with tsc output)');
  }
  const mod = lower(opts.module);
  if (['amd', 'umd', 'system', 'systemjs', 'none'].includes(String(mod))) {
    add('module', `"${String(opts.module)}" no longer exists`, '"preserve", "esnext" or "nodenext"');
  }
  if (lower(opts.target) === 'es5' || lower(opts.target) === 'es3') {
    add('target', 'ES5 is no longer a valid target', 'at least "ES2015", for Lambda preferably "ES2023"');
  }
  if ('baseUrl' in opts) {
    add('baseUrl', 'option was removed', 'write "paths" entries relative, starting with "./"');
  }
  const paths = (opts.paths ?? {}) as Record<string, string[]>;
  for (const [alias, targets] of Object.entries(paths)) {
    if (targets.some((t) => !t.startsWith('./') && !t.startsWith('../'))) {
      add(`paths["${alias}"]`, 'target without a leading "./"', 'e.g. "./src/shared/*" instead of "src/shared/*"');
    }
  }
  for (const flag of ['esModuleInterop', 'allowSyntheticDefaultImports', 'alwaysStrict']) {
    if (opts[flag] === false) add(flag, 'false is no longer allowed', 'delete the line');
  }
  if ('downlevelIteration' in opts) add('downlevelIteration', 'option was removed', 'delete the line');
  if ('outFile' in opts) add('outFile', 'option was removed', 'use a bundler such as esbuild');
  if ('ignoreDeprecations' in opts) {
    add('ignoreDeprecations', 'no longer helps in TypeScript 7', 'fix the actual causes');
  }
  if (!('types' in opts)) {
    add('types', 'default is now [] – @types packages are no longer loaded automatically', '"types": ["node"] (plus e.g. "jest")');
  }
  if (!('rootDir' in opts) && !opts.noEmit && sourceDirs.length === 1) {
    add('rootDir', 'default is now the tsconfig folder', `"rootDir": "./${sourceDirs[0]}"`);
  }
  return findings;
}

function sourceDirsOf(config: { include?: string[] }): string[] {
  const dirs = (config.include ?? []).map((p) => p.split('/')[0]).filter((d) => d && !d.includes('*'));
  return [...new Set(dirs)];
}

if (process.argv[1]?.endsWith('ts7-check.ts')) {
  const file = process.argv[2] ?? 'tsconfig.json';
  const config = parseJsonc(readFileSync(file, 'utf8')) as { compilerOptions?: Options; include?: string[] };
  const findings = checkCompilerOptions(config.compilerOptions ?? {}, sourceDirsOf(config));
  if (findings.length === 0) {
    console.log(`${file}: no known TypeScript 7 pitfalls found.`);
  } else {
    console.log(`${file}: ${findings.length} item(s) to fix before moving to TypeScript 7:`);
    for (const f of findings) console.log(`- ${f.option}: ${f.problem} → ${f.fix}`);
    process.exitCode = 1;
  }
}
Enter fullscreen mode Exit fullscreen mode

You run it with tsx, which you’ll need in a moment anyway. For the bike rental’s old configuration, the result looks like this:

npx tsx scripts/ts7-check.ts tsconfig.json
Enter fullscreen mode Exit fullscreen mode
tsconfig.json: 4 item(s) to fix before moving to TypeScript 7:
- moduleResolution: "node" no longer exists → "bundler" (with esbuild) or "nodenext" (with tsc output)
- baseUrl: option was removed → write "paths" entries relative, starting with "./"
- paths["@shared/*"]: target without a leading "./" → e.g. "./src/shared/*" instead of "src/shared/*"
- types: default is now [] – @types packages are no longer loaded automatically → "types": ["node"] (plus e.g. "jest")
Enter fullscreen mode Exit fullscreen mode

Step 3: Install TypeScript 7 and look at the first run

Now install TypeScript 7 and run the type check once. Expect errors; that’s the point of the exercise.

npm install --save-dev typescript@^7.0.2
npx tsc --noEmit
Enter fullscreen mode Exit fullscreen mode
tsconfig.json(6,25): error TS5108: Option 'moduleResolution=node10' has been removed. Please remove it from your configuration.
tsconfig.json(8,5): error TS5102: Option 'baseUrl' has been removed. Please remove it from your configuration.
  Use '"paths": {"*": ["./*"]}' instead.
tsconfig.json(9,30): error TS5090: Non-relative paths are not allowed. Did you forget a leading './'?
Enter fullscreen mode Exit fullscreen mode

Notice that TypeScript 7 only complains about the configuration at first. You’ll see errors in your code once the tsconfig is valid again. Under TypeScript 6, the same messages were still TS5107 and TS5101, and ignoreDeprecations could silence them.

Step 4: Fix modules and paths

For Lambda code that esbuild bundles, "moduleResolution": "bundler" together with "module": "preserve" is the right choice. This combination mirrors how bundlers resolve imports and allows imports without file extensions. If tsc emits JavaScript for Node.js itself, use "nodenext" for both options instead. baseUrl goes away, and the targets in paths start with ./. Here is the old configuration:

{
  // Typical configuration from the TypeScript 5 days
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "moduleResolution": "node",
    "lib": ["es2020"],
    "baseUrl": ".",
    "paths": { "@shared/*": ["src/shared/*"] },
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
  },
  "include": ["src/**/*.ts", "test/**/*.ts"]
}
Enter fullscreen mode Exit fullscreen mode

And here is the new one. Since esbuild does the building, it also sets noEmit: tsc no longer writes files and only checks.

{
  "compilerOptions": {
    "target": "ES2023",
    "lib": ["ES2023"],
    "module": "preserve",
    "moduleResolution": "bundler",
    "types": ["node"],
    "paths": { "@shared/*": ["./src/shared/*"] },
    "strict": true,
    "noEmit": true,
    "isolatedModules": true,
    "skipLibCheck": true
  },
  "include": ["bin", "lib", "src", "scripts", "test"]
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Bring back the Node types

If you leave out types, TypeScript no longer loads @types packages automatically since 6.0. In the Lambda handler you notice it right away at process.env:

src/rental.ts(5,17): error TS2591: Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
Enter fullscreen mode Exit fullscreen mode

The message already contains the fix: add "types": ["node"] to compilerOptions. If you use Jest with global functions like describe, add "jest" as well. The types from @types/aws-lambda need no entry, because you pull them in explicitly with import type.

Step 6: Set rootDir when tsc writes files itself

This step only affects you if tsc emits JavaScript, for example for a library or a container. TypeScript used to guess the common source folder. Now rootDir defaults to the folder containing tsconfig.json, and TypeScript 7 stops instead of quietly rearranging the output:

tsconfig.json(10,5): error TS5011: The common source directory of 'tsconfig.json' is './src'. The 'rootDir' setting must be explicitly set to this or another path to adjust your output's file layout.
  Visit https://aka.ms/ts6 for migration information.
Enter fullscreen mode Exit fullscreen mode

With "rootDir": "./src", src/rental.ts ends up in dist/rental.js again, not in dist/src/rental.js. That matters, because a Lambda handler path like dist/rental.handler would otherwise point nowhere.

Step 7: Replace ts-node with tsx in the CDK app

Now comes the part that costs the most time in older CDK projects. There, cdk.json typically starts the app with npx ts-node --prefer-ts-exts bin/app.ts. With TypeScript 7 in the project, it ends like this:

TypeError: Cannot read properties of undefined (reading 'fileExists')
    at readConfig (node_modules/ts-node/dist/configuration.js:91:33)
Enter fullscreen mode Exit fullscreen mode

ts-node accesses ts.sys from the compiler API, and that API is gone. tsx, on the other hand, transpiles TypeScript with esbuild and doesn’t need TypeScript at all. The change is a single line:

npm uninstall ts-node
npm install --save-dev tsx
Enter fullscreen mode Exit fullscreen mode
{
  "app": "npx tsx bin/app.ts",
  "watch": {
    "include": ["bin/**", "lib/**", "src/**"],
    "exclude": ["cdk.out", "node_modules", "test"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Because tsx doesn’t check types, npx tsc --noEmit now belongs in your CI pipeline for good. The current CDK template handles this by running npx tsc first and tsx second in cdk.json. That adds a little time to every cdk synth, but it prevents a type error from slipping into a deployment unnoticed.

Step 8: Tests without ts-jest

According to its package metadata, ts-jest requires TypeScript below version 7. You have two options. Either you switch to Vitest, which transpiles TypeScript itself and doesn’t need the compiler API. I show what that looks like for Lambda handlers in Lambda unit tests with Vitest. If you already have Jest tests and want to move them all, the step-by-step guide in Migrate Jest to Vitest walks you through it, check script included. Or you stay with Jest and only swap the transformer for @swc/jest, just like the new CDK template does. In the example project, handler logic, check script and CDK stack all run on Vitest:

// test/rental-stack.test.ts
import { App } from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import { describe, it } from 'vitest';
import { RentalStack } from '../lib/rental-stack';

describe('RentalStack', () => {
  it('creates a Node.js 24 function on ARM', () => {
    const template = Template.fromStack(new RentalStack(new App(), 'TestStack'));
    template.hasResourceProperties('AWS::Lambda::Function', {
      Runtime: 'nodejs24.x',
      Architectures: ['arm64'],
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

Step 9: Run ESLint with TypeScript 6 alongside

typescript-eslint is the tool you can’t easily swap out. Version 8.72 requires TypeScript from 4.8.4 and below 6.1.0. If you only install TypeScript 7, npm refuses:

npm error code ERESOLVE
npm error Could not resolve dependency:
npm error peer typescript@">=4.8.4 <6.1.0" from typescript-eslint@8.72.2
Enter fullscreen mode Exit fullscreen mode

The way out is two npm aliases. Under the name typescript you install the compatibility package with TypeScript 6, and under @typescript/native the new compiler. typescript-eslint finds its API this way, and npx tsc still starts TypeScript 7, because only the native package provides a command called tsc. In the terminal, the compatibility package is called tsc6.

npm install --save-dev "@typescript/native@npm:typescript@^7.0.2" "typescript@npm:@typescript/typescript6@^6.0.2"
npx tsc --version
npx tsc6 --version
Enter fullscreen mode Exit fullscreen mode
Version 7.0.2
Version 6.0.3
Enter fullscreen mode Exit fullscreen mode

The ESLint configuration stays as it was. In the example, only one rule is turned off, because Lambda handlers are async on purpose:

// eslint.config.mjs
import tseslint from 'typescript-eslint';

export default tseslint.config(
  { ignores: ['cdk.out/**', 'node_modules/**', 'eslint.config.mjs'] },
  ...tseslint.configs.recommendedTypeChecked,
  {
    languageOptions: {
      parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname },
    },
    // Lambda handlers are async on purpose, even if they don't await anything (yet)
    rules: { '@typescript-eslint/require-await': 'off' },
  },
);
Enter fullscreen mode Exit fullscreen mode

Step 10: Verify bundle and deployment

Finally, check that the end result is still the same. Types, tests and linting first, then cdk synth. In the test run, esbuild resolved the @shared/pricing alias cleanly and produced an ESM bundle of just under 500 bytes for Node.js 24 on ARM.

npx tsc --noEmit
npx vitest run
npx eslint .
npx cdk synth --quiet
Enter fullscreen mode Exit fullscreen mode

If everything is green, you can roll out with npx cdk deploy. Afterwards, a POST to /rentals/quote with {"hours": 3} returns {"station":"Central Station","priceCents":1350}. The costs stay tiny: a few test calls cost fractions of a cent on Lambda and the HTTP API, and the logs are deleted after two weeks. Still, don’t forget to tear the stack down again with npx cdk destroy.

The complete example with CDK and Serverless Framework

The handler is deliberately simple. It only uses the alias and process.env, which are exactly the two places where the migration typically gets stuck:

// src/rental.ts
import type { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from 'aws-lambda';
import { rentalPriceCents } from '@shared/pricing';

const STATION = process.env.STATION_NAME ?? 'Central Station';

export const handler = async (event: APIGatewayProxyEventV2): Promise<APIGatewayProxyResultV2> => {
  const body = JSON.parse(event.body ?? '{}') as { hours?: number };
  try {
    const priceCents = rentalPriceCents(body.hours ?? 1);
    return json(200, { station: STATION, priceCents });
  } catch (err) {
    return json(400, { message: (err as Error).message });
  }
};

function json(statusCode: number, payload: unknown): APIGatewayProxyResultV2 {
  return {
    statusCode,
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify(payload),
  };
}
Enter fullscreen mode Exit fullscreen mode

The CDK stack passes tsconfig.json to esbuild so that the alias is resolved in the bundle. It uses its own log group with 14 days of retention instead of the deprecated logRetention property. How to secure a stack like this with template assertions is covered in testing CDK stacks.

// lib/rental-stack.ts
import { CfnOutput, Duration, Stack, type StackProps } from 'aws-cdk-lib';
import { HttpApi, HttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
import { Architecture, Runtime } from 'aws-cdk-lib/aws-lambda';
import { NodejsFunction, OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs';
import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';
import type { Construct } from 'constructs';
import { fileURLToPath } from 'node:url';

export class RentalStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const rentalFn = new NodejsFunction(this, 'RentalFunction', {
      entry: fileURLToPath(new URL('../src/rental.ts', import.meta.url)),
      handler: 'handler',
      runtime: Runtime.NODEJS_24_X,
      architecture: Architecture.ARM_64,
      memorySize: 256,
      timeout: Duration.seconds(5),
      logGroup: new LogGroup(this, 'RentalLogs', { retention: RetentionDays.TWO_WEEKS }),
      environment: { STATION_NAME: 'Central Station' },
      bundling: {
        format: OutputFormat.ESM,
        minify: true,
        sourceMap: true,
        // esbuild reads "paths" from this tsconfig and resolves @shared/*
        tsconfig: fileURLToPath(new URL('../tsconfig.json', import.meta.url)),
      },
    });

    const api = new HttpApi(this, 'RentalApi');
    api.addRoutes({
      path: '/rentals/quote',
      methods: [HttpMethod.POST],
      integration: new HttpLambdaIntegration('RentalIntegration', rentalFn),
    });

    new CfnOutput(this, 'ApiUrl', { value: api.apiEndpoint });
  }
}
Enter fullscreen mode Exit fullscreen mode
// bin/app.ts
import { App } from 'aws-cdk-lib';
import { RentalStack } from '../lib/rental-stack';

const app = new App();
new RentalStack(app, 'BikeRentalStack');
Enter fullscreen mode Exit fullscreen mode

With Serverless Framework v4, there is nothing else to do for TypeScript 7, because the built-in esbuild step doesn’t call the TypeScript compiler. As with the CDK, type checking runs separately via npx tsc --noEmit:

# serverless.yml – alternative to the CDK stack (Serverless Framework v4)
service: bike-rental

provider:
  name: aws
  runtime: nodejs24.x
  architecture: arm64
  memorySize: 256
  timeout: 5
  logRetentionInDays: 14
  environment:
    STATION_NAME: Central Station

build:
  esbuild:
    bundle: true
    minify: true
    sourcemap: true
    # esbuild finds tsconfig.json on its own and resolves "paths" – tsc isn't needed for this

functions:
  rental:
    handler: src/rental.handler
    events:
      - httpApi:
          path: /rentals/quote
          method: post
Enter fullscreen mode Exit fullscreen mode

The package.json ties everything together, including both TypeScript aliases:

{
  "name": "typescript-7-migration",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "check:ts7": "tsx scripts/ts7-check.ts tsconfig.json",
    "typecheck": "tsc --noEmit",
    "lint": "eslint .",
    "test": "vitest run",
    "synth": "cdk synth"
  },
  "devDependencies": {
    "@types/aws-lambda": "^8.10.164",
    "@types/node": "^24.10.1",
    "@typescript/native": "npm:typescript@^7.0.2",
    "aws-cdk": "^2.1148.0",
    "esbuild": "^0.28.2",
    "eslint": "^9.39.5",
    "tsx": "^4.23.15",
    "typescript": "npm:@typescript/typescript6@^6.0.2",
    "typescript-eslint": "^8.72.2",
    "vitest": "^5.1.0"
  },
  "dependencies": {
    "aws-cdk-lib": "^2.276.0",
    "constructs": "^10.4.2"
  }
}
Enter fullscreen mode Exit fullscreen mode

Common errors in a TypeScript 7 migration

Message Cause Fix
TS5108 moduleResolution=node10 has been removed old "node" resolution "bundler" or "nodenext"
TS5102 baseUrl has been removed baseUrl used for path aliases delete it, start paths with ./
TS5090 Non-relative paths are not allowed target in paths without ./ "./src/shared/*"
TS2591 Cannot find name 'process' types is empty now "types": ["node"]
TS5011 The common source directory … rootDir not set "rootDir": "./src"
TypeError … reading 'fileExists' ts-node needs the compiler API tsx instead of ts-node
npm ERESOLVE … typescript-eslint peer dependency below 6.1 TypeScript 6 alongside via alias

One error doesn’t appear in any table, because it only shows up at runtime: when tsc emits JavaScript itself, aliases like @shared/pricing stay in the output, and Node.js can’t find them. That has always been the case and has nothing to do with TypeScript 7, but it tends to surface during the rework. For Lambda, let esbuild do the bundling, and the problem is gone.

My take: Switch now or wait for 7.1?

For Lambda and CDK projects, I would switch now. The effort is manageable, because esbuild does the actual work and tsc only checks. Above all, TypeScript 7 forces you to clean up legacy settings in your tsconfig that nobody understands anymore anyway. baseUrl and "moduleResolution": "node" date back to a time when Node.js had no ESM and bundlers like esbuild didn’t exist.

One honest catch remains: with two TypeScript versions in the project, package.json gets harder to read, and new team members stumble over tsc6. This is a transition, not a permanent state. I would briefly explain the alias in the README and create a ticket to remove it as soon as typescript-eslint officially supports TypeScript 7. Projects that rely heavily on Vue, Svelte or Angular templates are a different story. More tools depend on the API there, and waiting for 7.1 is a reasonable decision.

What I wouldn’t do: bundle the upgrade with a major update of Node.js, CDK and Jest in one pull request. If something breaks afterwards, you won’t know why. One branch, ten small commits, and each step above can be checked on its own.

FAQ

Is TypeScript 7 stable yet?

Yes. TypeScript 7.0 was released as a stable version in July 2026, and the current release is 7.0.2. Version 7.1 is in beta and is expected to bring a new compiler API.

Can I install TypeScript 6 and 7 at the same time?

Yes, using npm aliases. Put TypeScript 7 under @typescript/native and the compatibility package @typescript/typescript6 under the name typescript. Then npx tsc starts TypeScript 7, and tools like typescript-eslint find the old API.

Do I need to change the build for AWS Lambda?

Usually not. The AWS CDK with NodejsFunction and Serverless Framework v4 bundle with esbuild, which doesn’t need the TypeScript compiler. You only adjust tsconfig.json and check types separately with tsc --noEmit.

What replaces baseUrl in TypeScript 7?

Nothing, the option simply goes away. Path aliases in paths keep working, but their targets must be relative to tsconfig.json and start with ./, for example "./src/shared/*".

Your next step

Run the check script against the tsconfig.json of your largest TypeScript project today. It takes two minutes and tells you whether the migration will cost you an hour or a day. Then work through the steps above in a separate branch, one commit per step.

I’m curious: which tool is still holding you back from TypeScript 7, and have you found a solution yet? Let me know in the comments.

All code from this article, ready to run: smirowstanitzok/sascha-ms-examples

Sources

This post was created with AI assistance.


👉 Next step: Working with AWS CDK? Grab my printable AWS CDK cheat sheet with commands, concepts and a stack skeleton.

📬 Stay updated: New articles on serverless, AI and leading tech teams via RSS, browser push or the weekly newsletter (in German).

Originally published on sascha-ms.de. More articles on serverless, AI in everyday development and leading tech teams: sascha-ms.de/en

Top comments (0)