这是我参加「第五届青训营 」伴学笔记创造活动的第 12 天

TypeScript 介绍

  1. TypeScript 是 JavaScript 的超集,供给了 JavaScript 的一切功用,并供给了可选的静态类型、Mixin、类、接口和泛型等特性。
  2. TypeScript 的方针是经过其类型体系协助及早发现过错并进步 JavaScript 开发效率。
  3. 经过 TypeScript 编译器或 Babel 转码器转译为 JavaScript 代码,可运转在任何浏览器,任何操作体系。
  4. 任何现有的 JavaScript 程序都能够运转在 TypeScript 环境中,并只对其间的 TypeScript 代码进行编译。
  5. 在完整保存 JavaScript 运转时行为的基础上,经过引进静态类型界说来进步代码的可保护性,削减或许出现的 bug。
  6. 永远不会改动 JavaScript 代码的运转时行为,例如数字除以零等于 Infinity。这意味着,假如将代码从 JavaScript 迁移到 TypeScript ,即使 TypeScript 以为代码有类型过错,也能够保证以相同的办法运转。
  7. 对 JavaScript 类型进行了扩展,增加了例如 anyunknownnevervoid
  8. 一旦 TypeScript 的编译器完结了查看代码的作业,它就会 擦除 类型以生成最终的“已编译”代码。这意味着一旦代码被编译,生成的一般 JS 代码便没有类型信息。这也意味着 TypeScript 绝不会依据它揣度的类型更改程序的 行为。最重要的是,尽管或许会在编译过程中看到类型过错,但类型体系自身与程序怎么运转无关。
  9. 在较大型的项目中,能够在单独的文件 tsconfig.json 中声明 TypeScript 编译器的装备,并细化地调整其作业办法、严厉程度、以及将编译后的文件存储在何处。

编译选项

TypeScript 供给了许多不同功用的编译选项,既能够经过装备tsconfig.json文件中的compilerOptions特点来实现编译,也能够运用在tsc命令后跟随参数这种形式,直接编译.ts文件。

以下这些选项能够一同在命令行和 tsconfig.json 里运用。

选项 类型 默许值 描绘
–-allowJs boolean false 答应编译 JavaScript 文件
–-allowSyntheticDefaultImports boolean false 答应从没有设置默许导出的模块中默许导入
–-allowUnreachableCode boolean false 不陈述履行不到的代码过错
–-allowUnusedLabels boolean false 不陈述未运用的标签过错
–-alwaysStrict boolean false 以严厉形式解析并为每个源文件生成”use strict”句子
-–checkJs boolean false 在.js文件中陈述过错,与–allowJs配合运用
-–declaration -d boolean false 生成相应的.d.ts文件
-–declarationDir string 生成声明文件的输出途径
-–diagnostics boolean false 显现诊断信息
–-experimentalDecorators boolean false 启用实验性的ES装饰器
–-extendedDiagnostics boolean false 显现详细的诊断信息
–-forceConsistentCasingInFileNames boolean false 禁止对同一个文件的不一致的引用
–-inlineSourceMap boolean false 生成单个 sourcemaps 文件,而不是将每 sourcemaps 生成不同的文件
–-inlineSources boolean false 将代码与 sourcemaps 生成到一个文件中,要求一同设置了–inlineSourceMap或–sourceMap特点
–init 初始化 TypeScript 项目并创立一个tsconfig.json文件
–-listEmittedFiles boolean false 打印出编译后生成文件的姓名
–-listFiles boolean false 编译过程中打印文件名
–module -m string target == “ES6” ? “ES6” : “commonjs” 指定生成哪个模块体系代码:”None”、”CommonJS”、”AMD”、”System”、”UMD”、”ES6″ 或”ES2015″。
► 只需”AMD” 和”System” 能和–outFile 一同运用。
►”ES6″ 和”ES2015″ 可运用在方针输出为”ES5″ 或更低的情况下。
–moduleResolution string module == “AMD” or “System” or “ES6” ? “Classic” : “Node” 决定怎么处理模块
–noEmit boolean false 不生成输出文件
–noEmitHelpers boolean false 不在输出文件中生成用户自界说的协助函数代码,如__extends
–noEmitOnError boolean false 报错时不生成输出文件
–noErrorTruncation boolean false 不截短过错消息
–noFallthroughCasesInSwitch boolean false 陈述 switch 句子的 fallthrough 过错(即不答应 switch 的 case 句子贯穿)
–noImplicitAny boolean false 在表达式和声明上有隐含的any 类型时报错。
–noImplicitReturns boolean false 当不是函数的一切回来途径都有回来值时报错
–noImplicitThis boolean false 当this 表达式的值为any 类型时生成一个过错
–noImplicitUseStrict boolean false 模块输出中不包括”use strict” 指令
–noLib boolean false 不包括默许的库文件(lib.d.ts)
–noResolve boolean false 不把 /// <reference> 或模块导入的文件加到编译文件列表
–noStrictGenericChecks boolean false 禁用在函数类型里对泛型签名进行严厉查看
–noUnusedLocals boolean false 若有未运用的局部变量则抛错
–noUnusedParameters boolean false 若有未运用的参数则抛错
–outDir string 重定向输出目录
–-outFile string 将输出文件合并为一个文件,合并的顺序是依据传入编译器的文件顺序和 ///<reference> 和import的文件顺序决定的。
–preserveConstEnums boolean false 保存 const enum 声明
–preserveSymlinks boolean false 不把符号链接解析为其真实途径;将符号链接文件视为真正的文件
–preserveWatchOutput boolean false 保存 watch 形式下过时的控制台输出
–project -p string 编译指定目录下的项目,这个目录应该包括一个tsconfig.json文件来管理编译
–removeComments boolean false 删去一切注释,除了以/!* 最初的版权信息
–-skipDefaultLibCheck boolean false 疏忽库的默许声明文件的类型查看
–-skipLibCheck boolean false 疏忽一切的声明文件(*.d.ts)的类型查看
–sourceMap boolean false 生成相应的.map文件
–sourceRoot string 指定 TypeScript 源文件的途径,以便调试器定位。当 TypeScript 文件的方位是在运转时指定时运用此标记,途径信息会被加到sourceMap 里。
–strict boolean false 启用一切严厉类型查看选项,相当于启用–noImplicitAny、–noImplicitThis、–alwaysStrict、–strictNullChecks、–strictFunctionTypes 和 –strictPropertyInitialization。
–strictFunctionTypes boolean false 禁用函数参数双向协变查看
–strictPropertyInitialization boolean false 确保类的非 undefined 特点现已在构造函数里初始化,需求一同启用 –strictNullChecks。
–strictNullChecks boolean false 在严厉的null 查看形式下,null 和undefined 值不包括在任何类型里,只答应用它们自己和any 来赋值(有个破例,undefined 能够赋值到void)。
–target -t string ES3 指定 ECMAScript 方针版别ES3(默许)、ES5、ES6/ES2015、ES2016、ES2017 或ESNext。
留意:ESNext 最新的生成方针列表为ES proposed features。
–traceResolution boolean false 生成模块解析日志信息
–types string[] 要包括的类型声明文件名列表
–typeRoots string[] 要包括的类型声明文件途径列表
–-watch -w 在监督形式下运转编译器,会监督输出文件,在它们改动时从头编译。监督文件和目录的详细实现能够经过环境变量进行装备。

tsconfig.json

  1. 能够经过 tsc --init 命令在根目录生成 tsconfig.json 文件。
  2. 目录中存在 tsconfig.json 文件表示该目录是 TypeScript 项目的根目录。
  3. tsconfig.json 文件指定编译项目所需的根文件和编译器选项,主要有以下装备项:
{
  "compilerOptions": {},
  "files": [
    "core.ts",
    "index.ts",
    "types.ts"
  ],
  "exclude": [
    "node_modules", 
    "lib", 
    "**/*.test.ts"
  ],
  "include": [
    "src/**/*"
  ],
  "extends": "@tsconfig/recommended/tsconfig.json"
}
  • compilerOptions – 对象类型,用来设置编译选项,若不设置则默许运用上述编译选项的默许装备。
  • files – 指定一个包括相对或绝对文件途径的列表,不支撑 glob 匹配形式。
  • include – 指定一个文件 glob 匹配形式列表。
  • exclude – 扫除一个文件 glob 匹配形式列表。
  • extends – 字符串类型,指向另一个要承继的装备文件的途径。例如,能够承继一个推荐装备 npm i@tsconfig/recommended"extends": "@tsconfig/recommended/tsconfig.json"

    • 假如有同名装备,承继文件里的装备会掩盖源文件里的装备。
    • 装备文件里的相对途径在解析时相对于它地点的文件。

glob 通配符有:

  • * 匹配 0 或多个字符(不包括目录分隔符)
  • ? 匹配一个恣意字符(不包括目录分隔符)
  • **/ 递归匹配恣意子目录
  1. 假如一个 glob 形式里的某部分不包括文件扩展名(只包括 *.*),那么仅有支撑的文件扩展名类型被包括在内(默许情况下为 .ts、.tsx 和 .d.ts),假如 allowJs 设置为 true,也包括 .js 和 .jsx。
  2. 假如 filesinclude 都没有被指定,编译器默许包括当时目录和子目录下一切的 TypeScript 文件(.ts、.tsx 和 .d.ts),扫除在 exclude 里指定的文件。
  3. 假如一同指定了 filesinclude,编译器会将它们结合一并包括进来。
  4. 运用 include 引进的文件能够运用 exclude 特点过滤。然而,经过 files 特点清晰指定的文件却总是会被包括在内,不管 exclude 怎么设置。
  5. 运用 outDir 指定的目录下的文件永远会被编译器扫除,除非清晰地运用 files 将其包括进来(这时就算用 exclude 指定也没用)。
  6. 假如没有特别指定,exclude 默许情况下会扫除 node_modules、bower_components、jspm_packages 和 outDir 目录。
  7. 任何被 filesinclude 指定的文件所引用的文件也会被包括进来。例如,A.ts 引用了 B.ts,因而 B.ts 不能被扫除,除非引用它的 A.tsexclude 列表中。
  8. 编译器不会去引进那些或许作为输出的文件。例如,咱们包括了 index.ts,那么 index.d.tsindex.js 会被扫除在外。
  9. 优先级:命令行装备 > files > exclude > include

declaration

用来为工程中的每个 TypeScript 或 JavaScript 文件生成.d.ts文件,这些.d.ts文件是描绘模块外部 API 的类型界说文件。编辑东西能够经过.d.ts文件为非类型化的代码供给 intellisense 和精确的类型。

declaration设置为true时,用编译器履行下面的 TypeScript 代码:

export let helloWorld = "hi";

将会生成如下这样的index.js文件:

export let helloWorld = "hi";

以及一个相应的helloWorld.d.ts

export declare let helloWorld: string;

当运用.d.ts文件处理 JavaScript 文件时,需求运用 emitDeclarationOnlyoutDir 来确保 JavaScript 文件不会被掩盖。

strictFunctionTypes

  • 协变:答应子类型转换为父类型(能够里式替换 LSP 准则进行了解)。
  • 逆变:答应父类型转换为子类型。
  1. 在函数的参数类型中,是契合逆变的,函数的联系和参数的联系是相反的。
  2. 在老版别的 TS 中,函数参数是双向协变的。也便是说,既能够协变又能够逆变,可是这并不是类型安全的。
  3. 在新版别 TS(2.6+)中 ,能够经过敞开 strictFunctionTypes 来修正这个问题。设置之后,函数参数就不再是双向协变的了,函数参数查看更正确。

下面是一个禁用 strictFunctionTypes 的示例:

// @strictFunctionTypes: false
function fn(x: string) {
  console.log("Hello, " + x.toLowerCase());
}
type StringOrNumberFunc = (ns: string | number) => void;
// Unsafe assignment
let func: StringOrNumberFunc = fn;
// Unsafe call - will crash
func(10);

启用 strictFunctionTypes 后,将正确检测到过错:

// @strictFunctionTypes: true
function fn(x: string) {
  console.log("Hello, " + x.toLowerCase());
}
type StringOrNumberFunc = (ns: string | number) => void;
// Unsafe assignment
let func: StringOrNumberFunc = fn;
// Type '(x: string) => void' is not assignable to type 'StringOrNumberFunc'.
//   Types of parameters 'x' and 'ns' are incompatible.
//     Type 'string | number' is not assignable to type 'string'.
//       Type 'number' is not assignable to type 'string'.

在此功用的开发过程中,发现了大量本质上不安全的类层次结构,包括 DOM 中的一些。因而,该设置仅适用于以函数语法编写的函数,不适用于办法语法中的函数:

// @strictFunctionTypes: true
type Methodish = {
  func(x: string | number): void;
};
function fn(x: string) {
  console.log("Hello, " + x.toLowerCase());
}
// Ultimately an unsafe assignment, but not detected
const m: Methodish = {
  func: fn,
};
m.func(10);

typeAcquisition

对象类型,用以设置主动引进库类型界说文件(.d.ts),该特点下面有3个子特点:

  • enable: 布尔类型,用以设置是否敞开主动引进库类型界说文件
  • include: 数组类型,答应主动引进的库名列表,如["jquery", "kendo-ui"]
  • exclude: 数组类型,扫除的库名列表

代码提示的秘密 – d.ts

  1. 在运用 TypeScript 的时分,最大的一个优点便是能够给 JS 各种类型束缚,使得 JS 能够完结静态代码剖析,揣度代码中存在的类型过错或许进行类型提示。
  2. 而 TypeScript 完结类型揣度,需求事前知道变量的类型,假如咱们都是用 TypeScript 书写代码,而且给变量都指定了清晰的类型,TypeScript 是能够很好的完结类型揣度作业的。
  3. 可是有时,咱们不免会引进外部的 JS 库,这时 TypeScript 就对引进的 JS 文件里变量的详细类型不清晰了,为了告诉 TypeScript 变量的类型,因而就有了类型界说文件 d.ts(d 即 declare),TypeScript 的声明文件。
  4. 怎么让这些第三方库也能够进行类型推导呢?需求考虑怎么让 JS 库也能界说静态类型。JavaScript 和 TypeScript 的静态类型交叉口 — 类型界说文件,类似于 C/C++ 的 .h头文件(#include<stdio.h>),轻松让 JavaScript 也能支撑界说静态类型。
  5. d.ts 文件用于为 TypeScript 供给有关用 JavaScript 编写的 API 的类型信息。简略讲,便是你能够在 ts 文件中调用的 js 文件的声明文件。
  6. TypeScript 的核心在于静态类型,咱们在编写 TS 的时分会界说许多的类型,可是干流的库都是 JS 编写的,并不支撑类型体系。这个时分你不能用 TS 重写干流的库,咱们只需求编写仅包括类型注释的 d.ts 文件,然后在你的 TS 代码中,能够在仍然运用纯 JS 库的一同,取得静态类型查看的优势。
  7. 在此期间,解决的办法经过了许多的变化,从 DefinitelyTyped 到 typings(已停止保护)。最终是 @types。在 Typescript 2.0 之后,推荐运用 @types 办法,TypeScript 将会默许地查看 ./node_modules/@types 文件夹,主动从这儿来获取模块的类型界说,当然了,你需求独立装置这个类型界说。Microsoft 在The Future of Declaration Files介绍了 TypeScript 的这个新特性。

类型途径 – @types

  1. 默许情况下,一切的 @types 包都会在编译时应用,恣意层的 node_modules/@types 都会被运用,进一步说,在node_modules/@types中的任何包都被以为是可见的,这意味着包括了./node_modules/@types/../node_modules/@types/../../node_modules/@types/中一切的包。
  2. 假如你的类型界说不在上面这个默许文件夹中,能够运用 typesRoot 来装备,只需 typeRoots 下面的包才会被包括进来。例如:
{
  "compilerOptions": {
    "typeRoots": ["./typings", "./vendor/types"]
  }
}

这个装备文件将包括./typings./vendor/types下的一切包,而不包括./node_modules/@types下的。其间一切的途径都是相对于tsconfig.json

  1. types被指定,则只需列出的包才会被包括在大局范围内。例如:
{
  "compilerOptions": {
    "types": ["node", "jest", "express"]
  }
}

这个装备文件将只会包括./node_modules/@types/node./node_modules/@types/jest./node_modules/@types/express。其他在node_modules/@types/*下的包将不会被包括。此功用与 typeRoots 不同的是,它只指定你想要包括的详细类型,而 typeRoots 支撑你想要特定的文件夹。

  1. 能够指定 "types": [] 来禁用主动引进 @types 包。主动引进只在你运用了大局的声明(相反于模块)时是重要的,假如你运用import "foo" 句子,TypeScript 仍然会查找 node_modulesnode_modules/@types 文件夹来获取 foo 包。
  2. types选项不会影响@types/*怎么被包括在你的代码中,例如:
import * as moment from "moment";
moment().format("MMMM Do YYYY, h:mm:ss a");

moment导入会有完整的类型。当你设置了不在types数组中包括它们时,它将:

  • 不会在你的项目中增加大局声明(例如 node 中的process或 Jest 中的expect)。
  • 导出不会出现在主动导入的主张中。

d.ts 和 @types 的联系

@types 是 npm 的一个分支,用来寄存 d.ts 文件,假如对应的 npm 包寄存在 @types 中,要运用有必要下载!假如是自己本地的 d.ts 申明文件,则和 @types 没有任何联系!

实验

以下 baby.ts 文件,导出了一个 Baby 类,和一个叫 baby 的实例。Baby 包括一个私有的字段 _name,静态的办法 smile,公开的办法 getBabyName, 在经过 new 调用 constructor 的时分,会初始化咱们的 _name,而 getBabyName 便是拿到咱们私有的 _name,之所以需求 getBabyName,是因为经过 private 关键字指定的私有字段和办法,在实例中是无法访问的。

export class Baby {
  private _name: string;
  constructor(name: string) {
    this._name = name;
    console.log('小宝贝正在哭泣,哇哇哇哇哇~~~')
  }
  static smile() {
    console.log('O(∩_∩)O哈!')
  }
  getBabyName(): string {
    return this._name;
  }
}
export let baby = new Baby('Nico');

咱们加上 -d 选项编译 ts 文件:

tsc baby.ts -d

会有一个编译后的 baby.js 文件,你还会发现咱们多出了一个 baby.d.ts 文件。大多数 ts 初学者会这样问:请问一下,怎么在 ts 文件里边,引进现已写好的 js 文件呢?答案就在这儿,d.ts 文件。

export declare class Baby {
  private _name;
  constructor(name: string);
  static smile(): void;
  getBabyName(): string;
}
export declare let baby: Baby;

咱们发现 baby.ts 里边一切的办法声明都被导入到了 baby.d.ts 文件里边,而 TypeScript 恰恰便是经过这个 d.ts 文件进行代码提示的。

  1. 现在重命名一下咱们的 baby.ts,把它改成 baby.copy.ts
  2. 新建 main.ts 文件,当运用 import { baby } from "./baby"; 句子导入的时分,VSCode 会主动提示 baby.d.tsbaby.copy.ts
  3. 咱们选择 baby.d.tsbaby.js 模块文件的声明文件),然后再敲 baby.,此刻咱们就看到了 getBabyName 办法的提示。
  4. 假如删去 baby.d.ts 文件,会发现提示正告:无法找到模块“./baby”的声明文件。“baby.js”隐式具有 "any" 类型。

增加自己的 typings 文件夹

怎么解决没有库的 d.ts 文件时报错?

  1. 增加 typeRoots 装备项,就能够加载自己的 d.ts 文件了。
{
  "compilerOptions": {
    "typeRoots": ["typings"]
  }
}
  1. 在 typings 目录下新建一个 xxx.d.ts ,xxx 能够随意写。
declare module "koa" {
  interface Context {
    render(filename: string, ...args: any[]) : any;
    session: any;
    i18n: any;
    csrf: any;
    flash: any;
  }
}
  1. "koa" 便是你的报错库的称号,这儿就仅仅给 koa 库增加一些特点,避免代码编辑器报错。
  2. 还有一点要留意的是,报错必定是因为该包主目录下没有一个 index.js,或许放到 lib 目录下面了,新版别的 TypeScript 只需你装置了库,而且它的下面有 index.js 就能够加载到,不会报错可是会让你导入的是 any 类型。

怎么发布 d.ts 文件

  1. 第一种办法便是在你的库下面的 package.json 里边装备。这儿最好写上相对途径:
"types": "./lib/main.d.ts"
// or
"typings": "./lib/main.d.ts"

假如你的项目没有运用模块体系的话,能够将包中包括类型界说的 .d.ts 文件手动经过/// <reference path="" />引进。

  1. 第二种办法是给这个地址提交 PR。
https://github.com/DefinitelyTyped/DefinitelyTyped.git
  • 最近的构建都具有完善的类型标示:
  • 一切的包根据 typescript@next 版别都有完善的类型标示:
  • 一切的包都会在1小时30分钟内发布到 npm:
  • typescript-bot在 Definitely Typed 一向处于活跃状况

Definitely Typed

是一个高质量的 TypeScript 类型界说的仓库。

  1. npm 包中并不总是有可用的类型,或许有时项目不再保护,有时他们不感兴趣,或没有时间运用 TypeScript。
  2. 由于短少类型,在 TypeScript 中运用非类型化 npm 包将不会再具有类型安全性。
  3. 为了协助 TypeScript 开发人员运用这些包,有一个社区保护的项目叫做 Definitely Typed。
  4. Definitely Typed 是一个为没有类型的 NPM 包供给类型脚本界说的中央存储库的项目。
  5. 装置声明包后,一般不需求其他步骤来运用类型,TypeScript 会在运用包自身时主动选择类型。
npm install --save-dev @types/jquery
npm install --save-dev @types/node

编译器中会主动引进这些类型。假如你的项目没有运用模块体系的话,你或许需求运用types指令进行手动引用:

/// <reference types="node" />
  1. 当短少类型时,VSCode 等编辑器一般会主张装置此类包。对于 npm 包 “foo”,它的类型界说的包名应该是 “@types/foo”。假如没有找到你的包,请在TypeSearch查询。
  2. Definitely Typed 和 npm 上的@types包有什么联系?Definitely Typed GitHub 仓库 master分支 会经过DefinitelyTyped-tools主动发布到 npm 上的@types。