如何防止遮蔽全局界面?

How to prevent shadowing global interface?

我偶然发现了 Typescript 中 "shadowing" 全局接口的问题:

live-code-example

也就是下面的代码拒绝类型检查:

interface Location {
    location: string;
}

interface Named {
    name: string;
}

interface NamedLoc extends Named, Location {
    s: string;
}

let dd: NamedLoc = {
    s: '3',
    name: "asasf",
    location: "asd"
};

出现以下错误:

error TS2322: Type '{ s: string; name: string; location: string; }' is not assignable to type 'NamedLoc'.
  Property 'hash' is missing in type '{ s: string; name: string; location: string; }'.

13 let dd: NamedLoc = {
       ~~

注意,我定义了 MY OWN Location 接口, 但是 SOMEHOW 打字稿从以下位置获取了 Location 接口的定义:

interface Location {
    hash: string;
    host: string;
    hostname: string;
    href: string;
    readonly origin: string;
    pathname: string;
    port: string;
    protocol: string;
    search: string;
    assign(url: string): void;
    reload(forcedReload?: boolean): void;
    replace(url: string): void;
    toString(): string;
}

然而,这会进行类型检查!!?

interface Loc {
    location: string;
}

interface Named {
    name: string;
}

interface NamedLoc extends Named, Loc {
    s: string;
}

let dd: NamedLoc = {
    s: '3',
    name: "asasf",
    location: "asd"
};

我花了一些时间才弄清楚我定义的类型并没有在这里使用,而是必须进行其他事情,因为甚至我的编辑器也跳入了本地定义接口的定义。

如果将来引入另一种全局类型恰好与我当前应用程序中的一种类型同名,会发生什么情况?它会不再进行类型检查吗?

为什么会这样,背后到底发生了什么?

PS 我正在关注 TS-docs:

Extending-interfaces on ts-docs

这是对您所看到的内容的解释。它可以用这个简短的例子重现。

interface Location {
    location: string;
}

// Error!
const loc: Location = {
    location: ''
}

您遇到的是multiple declarations within the same common root contribute to the same type

因此,如果您的 Location 接口是全局的,它将添加到 lib.d.ts 的现有 Location 接口中。

您可以使用模块或命名空间简单地避免这种情况:

namespace Example {
    interface Location {
        location: string;
    }

    // This works
    const loc: Location = {
        location: ''
    }
}

如果您的文件导入或导出,它将是一个模块,不会有问题,就像在命名空间示例中一样。

这里发生的是未显示的接口声明合并,实际上是扩展内置类型的基本机制。如果您在全局范围内定义接口,则无法阻止这种情况。

您 运行 遇到的问题是经典的 Javascript 问题,即一切都在全局命名空间中。该问题的解决方案是使用模块,该解决方案同样适用于 Typescript。如果您使用模块,您的界面将不会与全局界面合并的风险。

另一种解决方案是将所有代码放在专用命名空间中,再次避免合并行为。