【问题标题】:In TypeScript, what does it mean for an import or export to be "top-level"?在 TypeScript 中,导入或导出为“顶级”意味着什么?
【发布时间】:2019-02-21 01:18:18
【问题描述】:

TypeScript docs 说:

在 TypeScript 中,就像在 ECMAScript 2015 中一样,任何包含顶级 importexport 的文件都被视为一个模块。相反,没有任何顶级 importexport 声明的文件被视为其内容在全局范围内可用的脚本(因此也可用于模块)。

导入或导出“顶级”是什么意思?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    顶级import 是位于文件最顶部的静态导入。然而,它被称为“顶级”并不是因为它位于文件的顶部,而是因为存在不是顶级的动态导入:

    import foo from 'foo' // top level import, static one
    
    import('foo').then(/* ... */) // not top level import, dynamic one
    
    // no static top-level import can live here (after any code that is not a top-level import declaration)
    
    function bar() {
      import('foo').then(/* ... */) // not top level import, dynamic one
      // no static top-level import can live here
      // no export can live here too
    }
    
    // no static top-level import can live here
    
    export const baz = 123 // exports are always top level, and static
    
    // You still can add not top level imports here, in the very end
    import('foo').then(/* ... */) 
    

    现在,为什么这在 Typescript 中很重要?

    如果你放置两个没有顶级导入/导出的文件,它们有两个相同的标识符,你会得到一个错误:

    // a.ts
    let foo = 1 // Error: duplicate identifier
    
    // b.ts
    let foo = 1 // Error: duplicate identifier
    

    发生这种情况是因为没有顶级导出/导入声明,并且 TS 认为这些文件是 scripts(与 modules 形成对比)。如果你在浏览器中加载两个具有相同标识符的脚本会发生什么?对,会出现“重复标识符”错误。因为这两个变量都存在于全局命名空间中。

    因此,为避免这种情况,您可以这样做:

    // a.ts
    let foo = 1 // Ok
    
    // b.ts
    let foo = 1 // Ok
    export {} // This is the magic. b.ts is now a module and hence, is not polluting the global namespace.
    

    【讨论】:

    • is a static import that is located at the very top of the file 这不是真的。导入的行号与是否为顶级无关。
    • @cgTag 我在下一句中写到了这一点。是的,在它们之前可能有空行或use strict 或 cmets,因此从技术上讲,它们可能不在最顶部
    【解决方案2】:

    typescript 中的顶层是最外层的作用域。

    什么是“范围”?

    每次打开一组{ 大括号¹时,您都会创建一个范围。 作用域将变量和函数的可见性限制在它们定义的作用域和子作用域内。

    例如:

    import { something } from "<module>"; <-- Global / Top-level scope
    
    function divide(x, y) { <-- Start function scope
      if(y == 0) { <-- Start of the if's scope
        /* 
         * Here is a child scope of the function
         * This means x and y are available here.
         */
    
        var error = new Error("Cannot divide by 0); <-- "error" is only available here.
        throw error;
      } <-- End of the if's scope
    
      /* 
       * The "error" variable is not available here
       * since the scope it was defined in, was already closed.
       */
    
      return x / y;
    } <-- Ends the functions scope
    
    var z = 0; <-- Global scope
    

    这意味着:

    import { x } from "<module>"
    /* rest of code */
    

    有效,但例如:

    if (true) {
      import { x } from "<module>";
    }
    

    不起作用,因为导入包含在 if 语句的范围内,因此不再处于顶级范围内。

    但这并不意味着“顶级”位于文件的顶部。它只是表示最外面的范围。

    function add(a, b) {
      return a + b;
    }
    
    import { x } from "<module>";
    

    这仍然有效,因为函数的范围以结束 } 大括号¹结束。意味着它再次处于顶层之后的所有内容。

    关于import 的所有内容也适用于export


    1:有时您可以省略{|} 大括号来创建新范围。您仍然会创建一个新范围,但会隐式执行此操作。 例如,考虑下面的两个 sn-ps - 它们是相同的。语言根据词法创建范围 - 范围不是由标记定义的

    if(true)
      return true;
    else
      return false;
    

    if(true) {
      return true;
    } else {
      return false;
    }
    

    【讨论】:

      猜你喜欢
      • 2019-11-18
      • 1970-01-01
      • 1970-01-01
      • 2017-03-25
      • 2015-04-30
      • 1970-01-01
      • 2023-01-03
      • 1970-01-01
      • 2012-11-06
      相关资源
      最近更新 更多