typescript 学习笔记2

模块 导出/导入

/**
 * 导出
 **/
// 当前文件有 export 内容,所以当前文件会被当成一个模块,
// 内部申明的变量不能直接被全局访问;
const modStr: string = "abc";

const expBol: boolean = true;

// 可以导出多种类型
export const expStr: string = "export";

export interface IExp {
    num: number;
}

export class CExp {
    str: string = "abc";
}

export type StrOrNum = string | number;

// 先申明后导出
export { expBol };

// 可以直接引导出引入的变量,进行重命名或拓展
// 也可以自己引入自己的变量
export { expBol as expBol2 } from "./export";

// 被直接导出的导入变量,当前模块代码无法访问
// expBol2; // Error

/**
 * 默认导出
 *
 * 一个模块只能有一个默认导出
 */
const bol: boolean = true;

export default bol;

// 不能这样简写(申明即导出)
// export default const bol = true; // Error

// 但是 类、函数、接口 等可以申明即导出
// export default class CS {}
// export default function() {}
// export default interface IF {}

// 字面值也可以直接默认导出
// export default true
// export default 'abc';
// export default 123;
// export default { num: 123 };

/**
 * 导入
 **/

// ts 文件中不带顶级的 import 或 export,
// 则内部申明的变量默认全局可访问;
num; // from advanceType.ts
num1; // from basicType.ts

// 无法直接访问具有 export 申明的 export.ts 文件(模块)中的变量
// modStr; // Error

// 导入需要加上路径,文件名后缀可省略
import { expStr, IExp, CExp, expBol, expBol2 } from "./export";

// 导入具有默认导出的模块,可以对默认内容重命名
import renamedBol from "./defaultExport";

// 导入模块所有内容到一个变量(对象)中
import * as exportObj from "./export";

// 导入类型变量的语法
import type { StrOrNum } from './export';

// 没有 export 的内容,也能直接通过 import './global' 的形式,
// 引入当前文件中的所有全局变量
const globalVar1: string = 'abc';
// 直接导入文件(没有包含 export),使引入文件中的所有全局变量
// 在当前文件可访问,例如:globalVar1
import "./global";

// 导入 export = xxx 形式的导出模块
import bol1 = require("./defaultExport");


let str: string = expStr;
str = globalVar1;

let bol: boolean = expBol;
bol = expBol2;
bol = renamedBol;
bol = exportObj.expBol2;
bol = bol1;

let obj1: IExp = {
    num: 123,
};
class CS1 extends CExp {}

let str2: StrOrNum = 'abc';


// 动态导入模块
// 首先申明 require 函数,或者直接安装申明文件:npm i -D @types/node
declare function require(name: string): any;
let bol2: boolean;

if (Math.random() > 0.5) {
    bol2 = require('./global');
} else {
    bol2 = false;
}

bol = bol2;


/**
 * 模块解析过程
 */

// 导入模块示例:
// import * from 'test';

// 相对导入:
//   带有路径标识,例如:'./test', '/test', '../test';
// 非相对导入:
//   不带路径标识,例如:'test', 'test/test', '@test/test';

// 相对导入:
// import * from './test'; 的解析模块文件顺序:
//
//  ./test.ts
//  ./test.tsx
//  ./test.d.ts
//  ./test/package.json (如果文件有指定 "types" 属性,则取它的值对应的文件)
//  ./test/index.ts
//  ./test/index.tsx
//  ./test/index.d.ts

// 非相对导入:
// import * from 'test'; 的解析模块文件顺序
//
//  ./node_modules/test.ts
//  ./node_modules/test.tsx
//  ./node_modules/test.d.ts
//  ./node_modules/test/package.json (如果文件有指定 "types" 属性,则取它的值对应的文件)
//  ./node_modules/test/index.ts
//  ./node_modules/test/index.tsx
//  ./node_modules/test/index.d.ts
//  ...(依次寻找上一级目录的 node_modules 文件夹)

// 配置文件中指定如何 解析非相对模块:
// {
//   "compilerOptions": {
//     "baseUrl": ".",
//     "paths": {
//       "test": [
//         "test/test", // 这里路径是相对于上面的 "baseUrl" 属性值
//         "test1/test1", // 可以指定多个回退路径,文件不存在会依次解析下一个
//         "*" // 模块名称与文件相同(当前路径下)可以直接使用通配符
//       ]
//     }
//   }
// }


命名空间

// ts 1.5 之后,内部模块(module x {})改为 命名空间(namespace x {}),
// 外部模块 改为 模块(import/export)

// 当前文件中或与其他文件中的全局变量中,存在重复的变量名申明会报错
// let num; // Error
let num7: number;
// let num7; // Error

// 通过命名空间解决这个问题
namespace Name1 {
  let num: number = 1;
}
namespace Name2 {
  // 同时,其他文件中也能这样重新申明同名变量;
  let num: number = 1;
}

// 但是变量和接口名可以重复,分别表示不同的结构
let CommonName;
interface CommonName {};

// 类、接口、命名空间也可以名称相同,都表示为同一个类型结构
class CommonName1 {};
interface CommonName1 {};
namespace CommonName1 {};


/**
 * 命名空间 嵌套
 */
namespace Name3 {
  namespace Name3_1 {
    let num: number = 1;
  }

  namespace Name3_2 {
    let num: number = 2;
  }

  // 变量之间互不影响
  let num: number = 3;
}

// 内部嵌套的命名空间外部无法访问
// Name3_1; // Error


/**
 * 命名空间 别名
 */
namespace Name4 {
  export namespace Name4_1 {
    export namespace Name4_2 {
      export const num: number = 1;
    }
  }
}

// 取别名的格式为:import xxx = name1.name2 ...
import Name42 = Name4.Name4_1.Name4_2;

// 访问别名中的变量就像访问对象
let num10: number = Name42.num;


/**
 * 外部命名空间
 */
declare namespace Name5 {
  export type num = number;
}
declare let num11: Name5.num;

发布

/************************************************************
 * 发布
 *
 * 发布声明文件的方式:
 * - 包含到 npm 包中;
 * - 发布到 @types 组织上
 ************************************************************/

/**
 * 包含到 npm 包中
 * 
 * 使用该 npm 包时可以不用添加额外的声明依赖("@types/xxx")到 "dependencies" 中
 */

// 配置 package.json 中的 "types" 属性(或者 "typings"),添加声明文件入口:
// {
//   "name": "demo",
//   "version": "0.1.1",
//   "main": "./src/main.ts",
//   "types": "./src/main.d.ts"
// }
//
// "main","types" 文件都有导出的话,最终只会导出 "types" 中的内容,
// 否则只会导出配置了的那个文件的导出内容;
//
// 只有声明入口文件和 "main" 文件同级并且同名(后缀 ".d.ts"),
// 才可以不用配置 "types" 值,ts 会自动识别它为声明文件入口;

// 也可以指定某些 ts 版本下使用的声明文件来源:
// {
//   "typesVersions": {
//     ">=3.1": {
//       "*": ["ts3.1/*"]
//     }
//     ">=3.0": {
//       "*": ["ts3.0/*"]
//     }
//   }
// }
// 该配置表示如果当前使用的 ts 版本大于等于 3.1 时,将从 "ts3.1"(与 package.json 文件同级)目录中读取声明文件,
// 然后是判断 3.0 版本,最后如果都没有匹配的,则再从 "types" 中读取声明文件;
// 注意版本的书写顺序,应该是高版本在上,低版本在下,否则会匹配错误;



/**
 * 发布到 @types 公共仓库中
 */

// 要单独发布自己的包对应的 types 包,先提交 PR 到 <https://github.com/DefinitelyTyped/DefinitelyTyped> 仓库中,
// types 包名称需要 npm 包对应,比如 demo 包对应的 types 包应该是 @types/demo;
// 然后使用发布工具 <https://github.com/microsoft/DefinitelyTyped-tools/tree/master/packages/publisher> 将 types 包发布到 npm 上;