这是我参加「第五届青训营 」伴学笔记创造活动的第 12 天
TypeScript 介绍
- TypeScript 是 JavaScript 的超集,供给了 JavaScript 的一切功用,并供给了可选的静态类型、Mixin、类、接口和泛型等特性。
- TypeScript 的方针是经过其类型体系协助及早发现过错并进步 JavaScript 开发效率。
- 经过 TypeScript 编译器或 Babel 转码器转译为 JavaScript 代码,可运转在任何浏览器,任何操作体系。
- 任何现有的 JavaScript 程序都能够运转在 TypeScript 环境中,并只对其间的 TypeScript 代码进行编译。
- 在完整保存 JavaScript 运转时行为的基础上,经过引进静态类型界说来进步代码的可保护性,削减或许出现的 bug。
- 永远不会改动 JavaScript 代码的运转时行为,例如数字除以零等于 Infinity。这意味着,假如将代码从 JavaScript 迁移到 TypeScript ,即使 TypeScript 以为代码有类型过错,也能够保证以相同的办法运转。
- 对 JavaScript 类型进行了扩展,增加了例如
any
、unknown
、never
、void
。 - 一旦 TypeScript 的编译器完结了查看代码的作业,它就会 擦除 类型以生成最终的“已编译”代码。这意味着一旦代码被编译,生成的一般 JS 代码便没有类型信息。这也意味着 TypeScript 绝不会依据它揣度的类型更改程序的 行为。最重要的是,尽管或许会在编译过程中看到类型过错,但类型体系自身与程序怎么运转无关。
- 在较大型的项目中,能够在单独的文件 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
- 能够经过
tsc --init
命令在根目录生成tsconfig.json
文件。 - 目录中存在
tsconfig.json
文件表示该目录是 TypeScript 项目的根目录。 -
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 或多个字符(不包括目录分隔符)?
匹配一个恣意字符(不包括目录分隔符)**/
递归匹配恣意子目录
- 假如一个 glob 形式里的某部分不包括文件扩展名(只包括
*
或.*
),那么仅有支撑的文件扩展名类型被包括在内(默许情况下为 .ts、.tsx 和 .d.ts),假如allowJs
设置为true
,也包括 .js 和 .jsx。 - 假如
files
和include
都没有被指定,编译器默许包括当时目录和子目录下一切的 TypeScript 文件(.ts、.tsx 和 .d.ts),扫除在exclude
里指定的文件。 - 假如一同指定了
files
或include
,编译器会将它们结合一并包括进来。 - 运用
include
引进的文件能够运用exclude
特点过滤。然而,经过files
特点清晰指定的文件却总是会被包括在内,不管exclude
怎么设置。 - 运用
outDir
指定的目录下的文件永远会被编译器扫除,除非清晰地运用files
将其包括进来(这时就算用exclude
指定也没用)。 - 假如没有特别指定,
exclude
默许情况下会扫除 node_modules、bower_components、jspm_packages 和outDir
目录。 - 任何被
files
或include
指定的文件所引用的文件也会被包括进来。例如,A.ts
引用了B.ts
,因而B.ts
不能被扫除,除非引用它的A.ts
在exclude
列表中。 - 编译器不会去引进那些或许作为输出的文件。例如,咱们包括了
index.ts
,那么index.d.ts
和index.js
会被扫除在外。 - 优先级:命令行装备 >
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 文件时,需求运用 emitDeclarationOnly
或 outDir
来确保 JavaScript 文件不会被掩盖。
strictFunctionTypes
- 协变:答应子类型转换为父类型(能够里式替换 LSP 准则进行了解)。
- 逆变:答应父类型转换为子类型。
- 在函数的参数类型中,是契合逆变的,函数的联系和参数的联系是相反的。
- 在老版别的 TS 中,函数参数是双向协变的。也便是说,既能够协变又能够逆变,可是这并不是类型安全的。
- 在新版别 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
- 在运用 TypeScript 的时分,最大的一个优点便是能够给 JS 各种类型束缚,使得 JS 能够完结静态代码剖析,揣度代码中存在的类型过错或许进行类型提示。
- 而 TypeScript 完结类型揣度,需求事前知道变量的类型,假如咱们都是用 TypeScript 书写代码,而且给变量都指定了清晰的类型,TypeScript 是能够很好的完结类型揣度作业的。
- 可是有时,咱们不免会引进外部的 JS 库,这时 TypeScript 就对引进的 JS 文件里变量的详细类型不清晰了,为了告诉 TypeScript 变量的类型,因而就有了类型界说文件
d.ts
(d 即declare
),TypeScript 的声明文件。 - 怎么让这些第三方库也能够进行类型推导呢?需求考虑怎么让 JS 库也能界说静态类型。JavaScript 和 TypeScript 的静态类型交叉口 — 类型界说文件,类似于 C/C++ 的
.h
头文件(#include<stdio.h>
),轻松让 JavaScript 也能支撑界说静态类型。 -
d.ts
文件用于为 TypeScript 供给有关用 JavaScript 编写的 API 的类型信息。简略讲,便是你能够在 ts 文件中调用的 js 文件的声明文件。 - TypeScript 的核心在于静态类型,咱们在编写 TS 的时分会界说许多的类型,可是干流的库都是 JS 编写的,并不支撑类型体系。这个时分你不能用 TS 重写干流的库,咱们只需求编写仅包括类型注释的
d.ts
文件,然后在你的 TS 代码中,能够在仍然运用纯 JS 库的一同,取得静态类型查看的优势。 - 在此期间,解决的办法经过了许多的变化,从 DefinitelyTyped 到 typings(已停止保护)。最终是 @types。在 Typescript 2.0 之后,推荐运用 @types 办法,TypeScript 将会默许地查看
./node_modules/@types
文件夹,主动从这儿来获取模块的类型界说,当然了,你需求独立装置这个类型界说。Microsoft 在The Future of Declaration Files介绍了 TypeScript 的这个新特性。
类型途径 – @types
- 默许情况下,一切的
@types
包都会在编译时应用,恣意层的node_modules/@types
都会被运用,进一步说,在node_modules/@types
中的任何包都被以为是可见的,这意味着包括了./node_modules/@types/
、../node_modules/@types/
、../../node_modules/@types/
中一切的包。 - 假如你的类型界说不在上面这个默许文件夹中,能够运用
typesRoot
来装备,只需typeRoots
下面的包才会被包括进来。例如:
{
"compilerOptions": {
"typeRoots": ["./typings", "./vendor/types"]
}
}
这个装备文件将包括./typings
和./vendor/types
下的一切包,而不包括./node_modules/@types
下的。其间一切的途径都是相对于tsconfig.json
。
- 当
types
被指定,则只需列出的包才会被包括在大局范围内。例如:
{
"compilerOptions": {
"types": ["node", "jest", "express"]
}
}
这个装备文件将只会包括./node_modules/@types/node
、./node_modules/@types/jest
和./node_modules/@types/express
。其他在node_modules/@types/*
下的包将不会被包括。此功用与 typeRoots
不同的是,它只指定你想要包括的详细类型,而 typeRoots
支撑你想要特定的文件夹。
- 能够指定
"types": []
来禁用主动引进@types
包。主动引进只在你运用了大局的声明(相反于模块)时是重要的,假如你运用import "foo"
句子,TypeScript 仍然会查找node_modules
和node_modules/@types
文件夹来获取foo
包。 -
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
文件进行代码提示的。
- 现在重命名一下咱们的
baby.ts
,把它改成baby.copy.ts
。 - 新建
main.ts
文件,当运用import { baby } from "./baby";
句子导入的时分,VSCode 会主动提示baby.d.ts
和baby.copy.ts
。 - 咱们选择
baby.d.ts
(baby.js
模块文件的声明文件),然后再敲baby.
,此刻咱们就看到了getBabyName
办法的提示。 - 假如删去
baby.d.ts
文件,会发现提示正告:无法找到模块“./baby”的声明文件。“baby.js”隐式具有 "any" 类型。
增加自己的 typings 文件夹
怎么解决没有库的 d.ts 文件时报错?
- 增加
typeRoots
装备项,就能够加载自己的 d.ts 文件了。
{
"compilerOptions": {
"typeRoots": ["typings"]
}
}
- 在 typings 目录下新建一个 xxx.d.ts ,xxx 能够随意写。
declare module "koa" {
interface Context {
render(filename: string, ...args: any[]) : any;
session: any;
i18n: any;
csrf: any;
flash: any;
}
}
-
"koa"
便是你的报错库的称号,这儿就仅仅给koa
库增加一些特点,避免代码编辑器报错。 - 还有一点要留意的是,报错必定是因为该包主目录下没有一个
index.js
,或许放到lib
目录下面了,新版别的 TypeScript 只需你装置了库,而且它的下面有index.js
就能够加载到,不会报错可是会让你导入的是any
类型。
怎么发布 d.ts 文件
- 第一种办法便是在你的库下面的
package.json
里边装备。这儿最好写上相对途径:
"types": "./lib/main.d.ts"
// or
"typings": "./lib/main.d.ts"
假如你的项目没有运用模块体系的话,能够将包中包括类型界说的 .d.ts
文件手动经过/// <reference path="" />
引进。
- 第二种办法是给这个地址提交 PR。
https://github.com/DefinitelyTyped/DefinitelyTyped.git
- 最近的构建都具有完善的类型标示:
- 一切的包根据 typescript@next 版别都有完善的类型标示:
- 一切的包都会在1小时30分钟内发布到 npm:
- typescript-bot在 Definitely Typed 一向处于活跃状况
Definitely Typed
是一个高质量的 TypeScript 类型界说的仓库。
- npm 包中并不总是有可用的类型,或许有时项目不再保护,有时他们不感兴趣,或没有时间运用 TypeScript。
- 由于短少类型,在 TypeScript 中运用非类型化 npm 包将不会再具有类型安全性。
- 为了协助 TypeScript 开发人员运用这些包,有一个社区保护的项目叫做 Definitely Typed。
- Definitely Typed 是一个为没有类型的 NPM 包供给类型脚本界说的中央存储库的项目。
- 装置声明包后,一般不需求其他步骤来运用类型,TypeScript 会在运用包自身时主动选择类型。
npm install --save-dev @types/jquery
npm install --save-dev @types/node
编译器中会主动引进这些类型。假如你的项目没有运用模块体系的话,你或许需求运用types
指令进行手动引用:
/// <reference types="node" />
- 当短少类型时,VSCode 等编辑器一般会主张装置此类包。对于 npm 包 “foo”,它的类型界说的包名应该是 “@types/foo”。假如没有找到你的包,请在TypeSearch查询。
- Definitely Typed 和 npm 上的@types包有什么联系?Definitely Typed GitHub 仓库 master分支 会经过DefinitelyTyped-tools主动发布到 npm 上的@types。