microsoft / microsoft/TypeScript

Scoped module declarations

未关闭
#60,751 4 条评论 13 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

Needs Proposal Suggestion
主要语言
Go
星标
111k
派生
14.4k
平均合并
1 天 19 小时
30 天内合并 PR
117

描述

🔍 Search Terms

scoped module declarations, scoped globals, scoping global types

✅ Viability Checklist
⭐ Suggestion

In todays web ecosystem that's getting more complex and feature-rich it's very hard to get the desired functionality by declaring normal modules. I will cover one primary example so you can understand the issue and how it would be solved, primarily through vitest global test context. Vitest offers you a way to use setup files to inject functions/other utilities into their context by using a beforeEach hook. The way you would type what you passed into it is like the following:

declare module "vitest" {
    export interface TestContext {
        foo: typeof foo
        bar: typeof bar
    }
}

this makes it globally available in all test suites. BUT, here's the problem. let's say you add integration tests as well, and you have another setup file that adds the following to the test context:

declare module "vitest" {
    export interface TestContext {
       integration: typeof integration
    }
}

Well, this creates an issue where whenever you try to access these globally defined contexts you have no idea if they are actually defined in the context they are running in, because the normal test suite has foo and bar, and the integration test suite has integration defined in the context. If you try to use foo in integration tests TS wouldn't complain but this would fail.

My idea is the following

// Define a unique `scope` keyword that scopes the following module augmentation to specific files that match a glob pattern
declare module "vitest" scope "**/*.unit.test.*" {
    export interface TestContext {
        foo: typeof foo
        bar: typeof bar
    }
}

This would override the module ONLY in files that match the scope, namely, in this case .unit.test.* files and then if we tried to use the foo or bar functionalities inside of there it wouldn't work. So to fix the problem from above, one would simply do the following:

// This overrides the unit.test suite
declare module "vitest" scope "**/*.unit.test.*" {
    export interface TestContext {
        foo: typeof foo
        bar: typeof bar
    }
}
// This overrides the integration.test suite
declare module "vitest" scope "**/*.integration.test.*" {
    export interface TestContext {
        integration: typeof something
    }
}

now in your integration tests you could do:

it("works", ({ integration }) => {
  // this is defined 
  integration.something
})

and in your unit tests:

it("works", ({ renderDevTools, integration }) => {
  // this is defined 
  renderDevTools
  // this is NOT defined and Typescript would complain
  integration
})

other useful usecases:

  • *.server.ts => make window object undefined
  • *.client.ts => make process object undefined
  • help framework authors / normal users declare globals in specific files where they are exposed
  • help framework authors / normal users inject custom utilities into specific functions/files
📃 Motivating Example
  • Allows you to create multiple overrides in specific contexts giving you actual typesafety instead of a potential footgun
  • Allows framework authors to exclude/include specific utilities/globals in places where they should be included/omitted for you, without you needing to guard against it (like window and process objects in SSR frameworks like Remix/React Router and Next.js)
  • Allows third party tooling to provide you with scoped globals without you having to configure anything manually
  • and more...
💻 Use Cases
  1. To make process object undefined in client only files
  2. To make window object undefined in server only files
  3. There are no workarounds for this, the only thing you could do is mark the object as undefined so you don't forget to add a ? in front of it so it doesn't crash your application if used somewhere where it's not supposed to be used

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

该 issue 没有指出 repository 文件、测试或入口点。首先查看 TypeScript 如何处理 module augmentation 和全局类型可用性,然后确定 scoped declarations 应如何应用于文件模式,以及超出作用域的 globals 应如何产生类型错误。

由索引模型根据 Issue 内容生成。

评估

技术栈
typescript
领域
compilers
Issue 类型
功能
难度
5/5
预计耗时
一周以上
活跃度
停滞
描述清晰度
基本清楚
新手友好度
30/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。