热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入理解TypeScript认识TypeScript&配置详解

为什么使用TypeScript?最初使用TypeScript开发项目的时候,觉得很繁琐,定义每一个变量还要加上类型,尤其是对象形式的,几乎每一个用到的都要定一个接口,还要为每一
为什么使用TypeScript?

最初使用TypeScript开发项目的时候,觉得很繁琐,定义每一个变量还要加上类型,尤其是对象形式的,几乎每一个用到的都要定一个接口,还要为每一个接口起一个易读的名字,写起来很麻烦。

但是,目前用TypeScript的人越来越多,尤其是一些大厂,大的项目。github上面很多开源项目也都是ts语法编译的。所以不得不重新考虑下它的价值,为什么会有那么多人选择使用它?

新建一个空文件夹。

我们都知道Javascript是一种弱类型语言,定义一个变量可以赋予任何类型的值:

// index.js
var js = '若类型语言'
js = true;

上面的写法是允许的。

在TypeScript中,这种写法是不允许的:

// foo.ts
var ts = '类型检查'
ts = true

即使我没有给ts变量定义类型,它也会被推断为string类型,只要有了类型,它就不能被赋值为其它类型。可以在编译阶段就发现大部分错误,这总比在运行时候出错好。

定义一个函数:

// foo.ts
function foo(type: string) {
return function (obj: any): boolean {
return Object.prototype.toString.call(obj) === `[object ${type}]`
}
}
foo('Object')

当调用这个函数的时候,可以很方便的看好这个函数的参数类型和返回值,从而知道使用方式。

 

综合意思可以总结TypeScript一些优点:

  • TypeScript 增加了代码的可读性和可维护性 §

  • 类型系统实际上是最好的文档,大部分的函数看看类型的定义就可以知道如何使用了
  • 可以在编译阶段就发现大部分错误,这总比在运行时候出错好

TypeScript还有一些特点:

  • TypeScript 是 Javascript 的超集,.js 文件可以直接重命名为 .ts 即可
  • 即使不显式的定义类型,也能够自动做出类型推论
  • 可以定义从简单到复杂的几乎一切类型
  • 即使 TypeScript 编译报错,也可以生成 Javascript 文件
  • 兼容第三方库,即使第三方库不是用 TypeScript 写的,也可以编写单独的类型文件供 TypeScript 读取

 

模块

全局模块

一个TypeScript项目中,使用的是新的方式写代码,但是运行在浏览器端的还是Javascript,所以需要将写好的ts文件编译成js文件,TypeScrip提供这样的功能。

全局安装:

npm install -g typescript

这样就可以全局执行tsc命令。 

新建一个ts文件:

const age: number = 18;

tsc index.ts ,生成一个index.js

// index.js
var age = 18;

 这时候你会看到index.ts文件报错了:

 

这是因为,默认情况下,当你开始在一个新的 TypeScript 文件中写下代码时,它处于全局命名空间中。也就是说可以在任何文件中访问这些变量,毋庸置疑,使用全局变量空间是危险的,因为它会与文件内的代码命名冲突。我们推荐使用下文中将要 提到的文件模块。

 

文件模块

如果在你的 TypeScript 文件的根级别位置含有 import 或者export ,它会在这个文件中创建一个本地的作用域。就不会再出现上面的问题。

user也报错了:

配置文件

在一个项目中,使用TypeScript来进行开发,最后都要编译成Javascript的,编译器如何进行编译呢,编译的规则是什么?都有哪些ts文件需要编译,哪些不需要编译?这时候就需要一个相关的配置文件,tsconfig.json。你可以在项目的根目录下手动创建这个文件,写入一些你需要的配置。

也可以使用命令生成这样的一个文件:

npm install -g typescript

以上命令会在全局环境下安装 tsc 命令,安装完成之后,我们就可以在任何地方执行 tsc 命令了。

进入你的项目,执行:

tsc --init

会在你的项目根目录下生成一个tsconfig.json文件,下面是一份tsconfig.json的说明:

{
"compilerOptions": {
/* 基本选项 */
"target": "es6", // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
"module": "commonjs", // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
"lib": [], // 指定要包含在编译中的库文件
"allowJs": true, // 允许编译 Javascript 文件
"checkJs": true, // 报告 Javascript 文件中的错误
"jsx": "preserve", // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
"declaration": true, // 生成相应的 '.d.ts' 文件
"sourceMap": true, // 生成相应的 '.map' 文件
"outFile": "./", // 将输出文件合并为一个文件
"outDir": "./", // 指定输出目录
"rootDir": "./", // 用来控制输出目录结构 --outDir.
"removeComments": true, // 删除编译后的所有的注释
"noEmit": true, // 不生成输出文件
"importHelpers": true, // 从 tslib 导入辅助工具函数
"isolatedModules": true, // 将每个文件做为单独的模块 (与 'ts.transpileModule' 类似).
/* 严格的类型检查选项 */
"strict": true, // 启用所有严格类型检查选项
"noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错
"strictNullChecks": true, // 启用严格的 null 检查
"noImplicitThis": true, // 当 this 表达式值为 any 类型的时候,生成一个错误
"alwaysStrict": true, // 以严格模式检查每个模块,并在每个文件里加入 'use strict'
/* 额外的检查 */
"noUnusedLocals": true, // 有未使用的变量时,抛出错误
"noUnusedParameters": true, // 有未使用的参数时,抛出错误
"noImplicitReturns": true, // 并不是所有函数里的代码都有返回值时,抛出错误
"noFallthroughCasesInSwitch": true, // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)
/* 模块解析选项 */
"moduleResolution": "node", // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
"baseUrl": "./", // 用于解析非相对模块名称的基目录
"paths": {}, // 模块名到基于 baseUrl 的路径映射的列表
"rootDirs": [], // 根文件夹列表,其组合内容表示项目运行时的结构内容
"typeRoots": [], // 包含类型声明的文件列表
"types": [], // 需要包含的类型声明文件名列表
"allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。
/* Source Map Options */
"sourceRoot": "./", // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
"mapRoot": "./", // 指定调试器应该找到映射文件而不是生成文件的位置
"inlineSourceMap": true, // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
"inlineSources": true, // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性
/* 其他选项 */
"experimentalDecorators": true, // 启用装饰器
"emitDecoratorMetadata": true // 为装饰器提供元数据的支持
}
}

 target

就是TypeScript文件编译后生成的Javascript文件里的语法应该遵循哪个Javascript的版本。可选项为:"ES5", "ES6""ES2015", "ES2016", "ES2017"或 "ESNext"

命令行创建出来的tsconfig.json文件,只有少量基本属性是开启的,举例:

"target": "es6", 
"module": "commonjs",
"strict": true,
"esModuleInterop": true,

运行 tsc,它会在当前目录或者是父级目录寻找 tsconfig.json 文件。

创建一个index.ts文件

function sayHello(person: string) {
return 'Hello, ' + person;
}
let user = 'Tom';
console.log(sayHello(user));

运行tsc index.ts,会生成一个编译好的index.js文件:

function sayHello(person) {
return 'Hello, ' + person;
}
var user = 'Tom';
console.log(sayHello(user));

会发现,即使配置了编译目标是ES6,编译出来的js文件,语法依然是es5。如果想tsconfig.json文件被启用,需要以下面的方式来编译:

  • 不带任何输入文件的情况下调用tsc,编译器会从当前目录开始去查找tsconfig.json文件,逐级向上搜索父目录。
  • 不带任何输入文件的情况下调用tsc,且使用命令行参数--project(或-p)指定一个包含tsconfig.json文件的目录。

当命令行上指定了输入文件时,tsconfig.json文件会被忽略。

文件根目录下直接执行tsc命令:

"use strict";
// index.ts
function sayHello(person) {
return 'Hello, ' + person;
}
let user = 'Tom';
console.log(sayHello(user));

 tsconfig.json文件中,"target": "es6", 所以编译好的js文件,也保留了es6语法。

因为ts文件不是以模块存在,会泄露全局变量,需要把ts文件变成模块:

// index.ts
export function sayHello(person: string) {
return 'Hello, ' + person;
}
export let user = 'Tom';
console.log(sayHello(user));

编译后:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.user = exports.sayHello = void 0;
// index.ts
function sayHello(person) {
return 'Hello, ' + person;
}
exports.sayHello = sayHello;
exports.user = 'Tom';
console.log(sayHello(exports.user));

module

就是你的TypeScript文件中的module,采用何种方式实现,可选项为:"None", "CommonJS", "AMD", "System", "UMD", "ES6"或 "ES2015"。

"module": "commonjs":

bar.ts 

export const apple: string = '小苹果'

编译后bar.js 

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.apple = void 0;
exports.apple = '小苹果';

 "module": "ES2015":

编译后的bar.js:

export const apple = '小苹果';

应该很容易就可以看明白这个属性的意义。指定了输出后文件的模块定义规范。 

这种模式下,ts文件中也不会出现重复生命变量或者函数的错误,是因为es6默认每个文件都是一个模块。

noEmit

noEmit: true,

执行tsc将不会看到有相应的js文件生成。

allowJs & checkJs

"allowJs": true

新增一个bar.js文件:

// bar.js
const add = '加'

执行 tsc

控制台报错,这个报错的原因就是js文件也会被编译一遍,并输出相应的js文件。

把noEmit设置为true,再执行tsc ,不会再出现上面的报错。但是,如果js文件有语法错误,控制台也不会有任何的提示。这是因为没有打开ji文件检查的开关。

// bar.js
var name = '麦乐'
console.log(age)

 checkJs: true,再执行会发先错误被抛出。

removeComments

这个比较容易理解, 设置为true后,,编译后的js文件没有注释,默认为false。

// index.ts
export function sayHello(person: string) {
return 'Hello, ' + person;
}
// 这里是注释

tsc

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sayHello = void 0;
function sayHello(person) {
return 'Hello, ' + person;
}
exports.sayHello = sayHello;

 outFile

 将输出文件合并为一个, outFile: './main.js'。执行tsc ,会提示Only 'amd' and 'system' modules are supported alongside --outFile。这里要把"module": "amd"。再次编译后输出main.js。

define("bar", ["require", "exports"], function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.apple = void 0;
exports.apple = '苹果';
});
define("index", ["require", "exports"], function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sayHello = void 0;
function sayHello(person) {
return 'Hello, ' + person;
}
exports.sayHello = sayHello;
});

"module": "system"。

System.register("bar", [], function (exports_1, context_1) {
"use strict";
var apple;
var __moduleName = context_1 && context_1.id;
return {
setters: [],
execute: function () {
exports_1("apple", apple = '苹果');
}
};
});
System.register("index", [], function (exports_2, context_2) {
"use strict";
var __moduleName = context_2 && context_2.id;
function sayHello(person) {
return 'Hello, ' + person;
}
exports_2("sayHello", sayHello);
return {
setters: [],
execute: function () {
}
};
});

 如果不想每次更改都重新执行tsc,可以执行tsc -w 。只要ts文件变化会自动重新编译。

outDir

输入文件的目录,不能和outFile同时使用,outDir: "./dist"。会在根目录下生成dist文件夹,编译后的js文件都会在这个文件夹中输出。

rootDir

用来指定编译文件的根目录,编译器会在根目录查找入口文件,如果编译器发现以rootDir的值作为根目录查找入口文件并不会把所有文件加载进去的话会报错,但是不会停止编译。

"rootDir": "./dist",会看到编译报错:

'/Users/artadmire/work/ts-test/bar.ts' is not under 'rootDir' '/Users/artadmire/work/ts-test/dist'. 'rootDir' is expected to contain all source files.

"rootDir": "./",则不会报错。

余下的属性会在深入理解的过程中介绍。

strictNullChecks

null检查,默认关闭。

noImplicitAny

选项 noImplicitAny 用来告诉编译器,当无法推断一个变量时发出一个错误(或者只能推断为一个隐式的 any 类型),你可以:

  • 通过显式添加 :any 的类型注解,来让它成为一个 any 类型;
  • 通过一些更正确的类型注解来帮助 TypeScript 推断类型。

模块引入
  • 可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加 载在这个对象上面:

bar.ts

export const foo = 123;
export function fun() {
console.log('fun')
}

foo.ts

import * as React from './bar'
console.log(React)

执行tsc foo.ts编译出js文件,然后node foo.js

增加默认导出:

bar.ts

export const foo = 123;
export function fun() {
console.log('fun')
}
const React = {};
export default React

tsc foo.ts
node foo.js

 编译后的foo.js

"use strict";
exports.__esModule = true;
var React = require("./bar");
console.log(React);

可以看到,所有的导出都被加在了React对象上面。

  • 你可以重命名变量导出:

// bar.ts
const someVar = 123;
export { someVar as aDifferentName };

 

  • 重命名导出变量或者类型:

// foo.ts
2. import { someVar as aDifferentName } from './bar';

  • 从其他模块导入后整体导出:

export * from './foo';

  • 从其他模块导入后,部分导出:

export { someVar } from './foo';

当导入路径不是相对路径时,模块解析将会模仿 Node 模块解析策略 ,以下我将给出一个简单例 子:

当你使用 import * as foo from 'foo' ,将会按如下顺序查找模块:

  • ./node_modules/foo
  • ../node_modules/foo
  • ../../node_modules/foo

直到系统的根目录
当你使用 import * as foo from 'something/foo' ,将会按照如下顺序查找内容

  • ./node_modules/something/foo
  • ../node_modules/something/foo
  • ../../node_modules/something/foo

直到系统的根目录

什么是 place?

当我提及被检查的 place 时,我想表达的是在这个 place ,TypeScript 将会检查以下内容 (例如一个 foo 的位置):

如果这个 place 表示一个文件,如: foo.ts ,欢呼!
否则,如果这个 place 是一个文件夹,并且存在一个文件 foo/index.ts ,引入的就是这个index.ts文件。

否则,如果这个 place 是一个文件夹,并且存在一个 foo/package.json 文件,在该文件 中指定 types 的文件存在,引入的就是package.json指定的type文件。.d.ts
否则,如果这个 place 是一个文件夹,并且存在一个 package.json 文件,在该文件中指 定 main 的文件存在,引入的就是main属性对应的文件。.js
从文件类型上来说,我实际上是指 .ts , .d.ts 或者 .js

 

声明空间

在 TypeScript 里存在两种声明空间:类型声明空间与变量声明空间。我将会在下文中和大家讨论这 两个概念。

类型声明空间

类型声明空间包含用来当做类型注解的内容,例如以下的一些类型声明:

class Foo {}
interface Bar {}
type Bas = {};

你可以将 Foo , Bar , Bas 做为类型注解使用,但是不能用做变量。

let foo: Foo;
let bar: Bar;
let bas: Bas;

Bar并未定义在变量声明空间。

变量声明空间

变量声明空间包含可用作变量的内容,在上文中 Class Foo 提供了一个类型 Foo空间,此外它同样提供了一个变量 Foo 到变量声明空间,如下所示:

一些像你用 var 声明的变量,也仅能在变量声明空间使用,不能用作类型注解。

 
推荐阅读
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 枚举使用枚举我们可以定义一些带名字的常量。使用枚举可以清晰地表达意图或创建一组有区别的用例。TypeScript支持数字的和基于字符串的枚举。数字枚举首先我们看看数字枚举,如果你使 ... [详细]
  • 前言小伙伴们大家好。从今天开始我们将从 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Vue3中setup函数的参数props和context配置详解
    本文详细介绍了Vue3中setup函数的参数props和context的配置方法,包括props的接收和配置声明,以及未通过props进行接收配置时的输出值。同时还介绍了父组件传递给子组件的值和模板的相关内容。阅读本文后,读者将对Vue3中setup函数的参数props和context的配置有更深入的理解。 ... [详细]
  • Python已成为全球最受欢迎的编程语言之一,然而Python程序的安全运行存在一定的风险。本文介绍了Python程序安全运行需要满足的三个条件,即系统路径上的每个条目都处于安全的位置、"主脚本"所在的目录始终位于系统路径中、若python命令使用-c和-m选项,调用程序的目录也必须是安全的。同时,文章还提出了一些预防措施,如避免将下载文件夹作为当前工作目录、使用pip所在路径而不是直接使用python命令等。对于初学Python的读者来说,这些内容将有所帮助。 ... [详细]
  • 小编给大家分享一下TypeScript2.7有什么改进,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收 ... [详细]
  • TS-入门学习笔记TypeScript是JavaScript的一个超集,主要提供了类型系统和对ES6的支持。与js相比,最大的有点是类型系统的引入,由于js本身是弱类型语言,所以天 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文介绍了在Go语言中可见性与scope的规则,包括在函数内外声明的可见性、命名规范和命名风格,以及变量声明和短变量声明的语法。同时,还介绍了变量的生命周期,包括包级别变量和局部变量的生命周期,以及变量在堆和栈上分配的规则和逃逸分析的概念。 ... [详细]
author-avatar
七楼居民_651
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有