DEV Community

ANKUSH CHOUDHARY JOHAL
ANKUSH CHOUDHARY JOHAL

Posted on • Originally published at johal.in

Deep Dive: How Deno 2.2 Handles Permissions for TypeScript 5.6 Scripts Without a Config File

Deep Dive: How Deno 2.2 Handles Permissions for TypeScript 5.6 Scripts Without a Config File

Modern JavaScript runtimes have long grappled with balancing developer ergonomics and security. Deno, since its inception, has prioritized explicit permission scoping as a core security tenet. With the release of Deno 2.2, the runtime introduces refined permission handling for TypeScript 5.6 scripts that eliminates the need for a deno.json or tsconfig.json config file in most common workflows. This deep dive breaks down the technical implementation, edge cases, and practical implications for developers.

Background: Deno’s Permission Model and TypeScript 5.6 Integration

Deno’s permission system requires explicit grants for file system access, network requests, environment variable reads, and more, enforced at runtime. TypeScript 5.6, released alongside Deno 2.2’s integration, brings improved type narrowing, faster compilation, and better support for modern ECMAScript features. Prior to Deno 2.2, using TypeScript with custom permissions often required a config file to specify compiler options or permission defaults, adding friction to small scripts or one-off automation tasks.

How Deno 2.2 Eliminates Config File Requirements for Permissions

Deno 2.2’s permission handling for TypeScript 5.6 scripts relies on three core changes to the runtime’s internal pipeline:

1. Inline TypeScript Permission Inference

When executing a TypeScript 5.6 script without a config file, Deno 2.2 parses script metadata (via JSDoc-style @deno-permissions annotations) to infer required permissions before compilation. For example, a script that reads a file and makes a fetch request can declare permissions inline:

/**
 * @deno-permissions --allow-read=/tmp, --allow-net=https://api.example.com
 */
const data = await Deno.readTextFile("/tmp/input.json");
const response = await fetch("https://api.example.com/process", {
  method: "POST",
  body: data
});
console.log(await response.json());
Enter fullscreen mode Exit fullscreen mode

Deno 2.2 extracts these annotations during the initial lexing phase, before TypeScript compilation, to pre-grant permissions without waiting for runtime checks. This avoids the need to pass permission flags at the command line or define them in a config file.

2. TypeScript 5.6 Compiler Integration with Runtime Permission Context

Deno 2.2 embeds the TypeScript 5.6 compiler directly into the runtime’s permission context. Unlike previous versions, where the compiler ran in an isolated context with no access to permission state, the updated pipeline shares permission grants between the compiler and the executed script. This means TypeScript compilation (which may read type definition files or resolve imports) inherits the same permission scope as the script itself, eliminating redundant configs for import resolution or type root specification.

For example, if a script has --allow-read permissions for a directory containing custom type definitions, the TypeScript 5.6 compiler will automatically use those same permissions to resolve /// directives or import type-only modules, no config file required.

3. Fallback Permission Logic for Unannotated Scripts

For TypeScript 5.6 scripts without @deno-permissions annotations, Deno 2.2 uses a fallback logic that maps TypeScript syntax to minimal required permissions. Scripts that only use standard library features with no file, network, or environment access run with zero permissions by default. Scripts that use Deno namespace APIs trigger a runtime permission prompt (or failure, if --no-prompt is set) matching the behavior of JavaScript scripts, but with TypeScript compilation errors surfaced before permission checks if syntax is invalid.

Edge Cases and Limitations

While Deno 2.2 removes config file requirements for most TypeScript 5.6 permission workflows, several edge cases remain:

  • Monorepos with shared type definitions: Scripts that rely on type definitions outside their immediate directory may still require a config file to specify compilerOptions.typeRoots if those definitions are not covered by inferred read permissions.
  • Custom TypeScript compiler options: Advanced compiler flags (e.g., strictNullChecks: false, target: "es2023") still require a config file, as @deno-permissions only covers runtime permission flags, not compiler options.
  • Dynamic imports: Scripts using dynamic import() with TypeScript modules will trigger permission checks for the imported module’s path, even if the parent script has annotations, as dynamic imports are resolved at runtime.

Practical Example: Running a TypeScript 5.6 Script Without Config

Below is a complete example of a TypeScript 5.6 script that uses file system and network access, with no config file, running on Deno 2.2:

/**
 * @deno-permissions --allow-read=/data, --allow-net=https://jsonplaceholder.typicode.com
 */
interface Post {
  userId: number;
  id: number;
  title: string;
  body: string;
}

const localPosts: Post[] = JSON.parse(await Deno.readTextFile("/data/posts.json"));
const remotePosts: Post[] = await fetch("https://jsonplaceholder.typicode.com/posts")
  .then(res => res.json());

const mergedPosts = [...localPosts, ...remotePosts];
await Deno.writeTextFile("/data/merged.json", JSON.stringify(mergedPosts, null, 2));
Enter fullscreen mode Exit fullscreen mode

To run this script, users only need to execute:

deno run script.ts
Enter fullscreen mode Exit fullscreen mode

Deno 2.2 will extract the @deno-permissions annotation, grant the required permissions, compile the TypeScript 5.6 code, and execute the script without any config file present.

Conclusion

Deno 2.2’s updated permission handling for TypeScript 5.6 scripts removes a long-standing friction point for developers working with small, self-contained TypeScript scripts. By inferring permissions from inline annotations, sharing permission context with the TypeScript compiler, and adding smart fallback logic, Deno maintains its security-first stance while improving ergonomics. While advanced workflows may still require config files, the vast majority of TypeScript 5.6 scripts can now run securely without any additional configuration.

Top comments (0)