文章目录
1. 初始命令
初始化项目: tsc --init
会创建一个tsconfig.json
配置文件。
编译ts文件: tsc demo.ts
将.ts文件转换成demo.js
文件。
执行
tsc demo.ts
并不会读取tsconfig.json
文件配置,只有执行tsc
才会根据配置文件信息编译ts文件
tsconfig.json
配置项:
配置 | 功能 |
---|---|
removeComments | 移除注释 |
noImplicitAny | false :不必指定所有数据类型 (在strict为false的情况下) |
strictNullChecks | false: 不对null值进行校验 |
incremental | 增量式编译 |
allowJs | 允许对js文件进行编译 |
checkJs | 对js文件进行语法检测 |
noUnusedLocals | 对未使用的变量进行检测 |
2. 类型保护
as
: 通过自身对代码的理解,确保代码不会出错
// 联合类型的类型保护
interface Bird {
fly: boolean;
sing: () => {};
}
interface Dog {
fly: boolean;
bark: () => {};
}
// 类型断言1: 变量名 as 类型
function trainAnimal(animal: Bird | Dog) {
if (animal.fly) {
(animal as Bird).sing();
} else {
(animal as Dog).bark();
}
}
// 类型断言2:<类型>变量名
let length: number = (<string>value).length;
// 类型保护:in
function trainAnimalSecond(animal: Bird | Dog) {
if ('sing' in animal) {
animal.sing();
} else {
animal.bark();
}
}
// 类型保护:typeof
function add(first: string | number, second: string | number) {
if (typeof first === 'string' || typeof second === 'string') {
return `${first}${second}`;
}
return first + second;
}
// 类型保护:instanceof
// 这里必须用class类来定义,只有类能用instanceof调用
class NumberObj {
count: number;
}
function addSecond(first: object | NumberObj, second: object | NumberObj) {
if (first instanceof NumberObj && second instanceof NumberObj) {
return first.count + second.count;
}
return 0;
}
3. 枚举类型
enum Status {
OFFLINE = 1, // 设置值为1(枚举默认从0开始)
ONLINE,
DELETED
}
// key value 可形成映射关系
console.log(Status.OFFLINE, Status[1]); // 1 OFFLINE
4. 泛型
- 函数泛型
// 泛型 generic 泛指的类型,根据调用的时候传递的参数类型
function join<T, P>(first: T, second: P) {
return `${first}${second}`;
}
join<number, string>(1, '1'); // 显式指定T和P的类型
join(1, '1'); // ts自动根据传递的参数推断
// T[]
function map<T>(params: Array<T>) {
return params;
}
map<string>(['123']);
- 类中的泛型
interface Item {
name: string;
}
// 泛型有一个name属性
class DataManager<T extends Item> {
constructor(private data: T[]) {}
// 返回的是string类型
getItem(index: number): string {
return this.data[index].name;
}
}
// 传递一个对象的数组
const data = new DataManager([{ name: 'dell' }]);
data.getItem(0);
5. namespace 命名空间
避免使用过多全局变量。(类模块化的方式)
- 使用命名空间
namespace Home {
class Header {
constructor() {
const elem = document.createElement('div');
elem.innerText = 'This is Header';
document.body.appendChild(elem);
}
}
class Content {
constructor() {
const elem = document.createElement('div');
elem.innerText = 'This is Content';
document.body.appendChild(elem);
}
}
// 命名空间中暴露出的变量,用于外部使用
export class Page {
constructor() {
new Header();
new Content();
}
}
}
使用: new Home.Page();
- 命名空间引用
/* namespace相互依赖的声明:Home命名空间依赖components.ts的内容 */
///<reference path="components.ts" />
namespace Home {
export class Page {
constructor() {
new Components.Header();
new Components.Content();
}
}
}
tsconfig.js
将outFile
设置到一个文件中就可以使用其他文件下的命名空间:
{
"compilerOptions": {
"target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
"module": "amd" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
// 将所有输出的文件打包到一个文件中,此时module不支持commonjs模块化的这种方式, 只支持amd/umd
"outFile": "./dist/page.js" /* Concatenate and emit output to single file. */,
"outDir": "./dist" /* Redirect output structure to the directory. */,
"rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
}
}
6. parcel
: 直接编写ts代码
html 中引入的ts代码进行编译成浏览器能够识别的代码。
文件路径:src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<script src="./page.ts"></script>
</head>
<body></body>
</html>
运行:parcel ./src/index.html
7. .d.ts
类型定义文件:为了帮助理解ts文件
- 变量类型定义
page.ts
文件:
$(function () {
$('body').html('<div>123</div>');
});
针对上述ts
文件,对应的jquery.d.ts
内容:
// 定义全局变量: $ 接收的参数是一个函数
// declare var $: (param: () => void) => void;
// 定义全局函数
interface JqueryInstance {
html: (html: string) => JqueryInstance;
}
// 函数重载:允许对同一个函数名字$,写全局函数的声明
declare function $(readyFunc: () => void): void;
declare function $(selector: string): JqueryInstance;
// 或 使用interface实现函数重载
interface JQuery {
(readyFunc: () => void): void;
(selector: string): JqueryInstance;
}
declare var $:JQuery;
-
声明全局变量
TypeScript declare的用法declare var wx: any; // declare 的意思是声明,var 的全称是 variable 表示变量。wx 是全局变量的名称,而 any 表示该变量的类型。
-
如何对对象以及类进行类型定义
ts
文件: new $.fn.init();
对应的类型jquery.d.ts
文件:
// $命名空间下的fn命名空间中有一个init的构造函数
declare namespace $ {
namespace fn {
class init {}
}
}
- ES6 模块化的类型
ts
文件:
import $ from 'jquery';
$(function() {
$('body').html('<div>123</div>');
new $.fn.init();
});
jquery.d.ts
文件:declare module
// Es6 模块化的描述文件
declare module 'jquery' {
interface JqueryInstance {
html: (html: string) => JqueryInstance;
}
// 混合类型
function $(readyFunc: () => void): void;
function $(selector: string): JqueryInstance;
namespace $ {
namespace fn {
class init {}
}
}
export = $;
}
8. 泛型中typeof的使用
interface Person {
name: string;
age: number;
gender: string;
}
// keyof Person : 对Person中的接口值进行遍历
class Teacher {
constructor(private info: Person) {}
getInfo<T extends keyof Person>(key: T): Person[T] {
return this.info[key];
}
}
// 使用解释:(type 可以是字符串)
// (T extends keyof Person): type T = 'gender'
// (key: T) : key: 'gender'
// (Person[T]) : Person['gender']
const teacher = new Teacher({
name: 'dell',
age: 18,
gender: 'male'
});
const test = teacher.getInfo('name');
console.log(test);
这样就限制了getInfo 的入参只能是Person中定义的三个变量,并且能够确定返回值类型。
9. 对项目中的一些依赖的使用
- 根据业务场景获取时需要增加准确的类型描述
import { Router, Request, Response } from 'express';
// 继承原有类型描述
interface RequestWithBody extends Request {
body: {
[key: string]: string | undefined;
};
}
router.post('/getData', (req: RequestWithBody, res: Response) => {
const { password } = req.body; // 业务场景值
if (password === '123') {
res.send('getData Success!');
} else {
res.send(`${req.teacherName} password Error!`);
}
});
- 设置时使用类型融合
import express, { Request, Response, NextFunction } from 'express';
app.use((req: Request, res: Response, next: NextFunction) => {
// 当设置的值是依赖中不存在的变量时,可以定义custom.d.ts文件,使用类型融合
req.teacherName = 'dell';
next();
});
custom.d.ts
类型定义,ts会进行类型融合:
// 类型融合
declare namespace Express {
interface Request {
teacherName: string;
}
}
10. 装饰器
装饰器本身是一个函数,类装饰器接受的参数是构造函数,装饰器通过 @ 符号来使用。装饰器在类创建好之后就立即执行。
tsconfig.json
配置支持装饰器 。
{
"compilerOptions": {
"experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
"emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
}
}
- 类的装饰器
// 通过返回一个函数的方式来接收变量
function testDecorator(flag: boolean) {
if (flag) {
return function(constructor: any) {
constructor.prototype.getName = () => {
console.log('dell');
};
};
} else {
return function(constructor: any) {};
}
}
@testDecorator(true)
class Test {}
const test = new Test();
(test as any).getName();
- 方法装饰器
// 普通方法,target 对应的是类的 prototype
// 静态方法,target 对应的是类的构造函数
function getNameDecorator(target: any, key: string, descriptor: PropertyDescriptor) {
// console.log(target, key);
// descriptor.writable = true; // 装饰器的方法是否允许修改
descriptor.value = function() { // 修改方法的返回值
return 'decorator';
};
}
class Test {
name: string;
constructor(name: string) {
this.name = name;
}
@getNameDecorator
getName() {
return this.name;
}
}
const test = new Test('dell');
console.log(test.getName());
方法的错误捕获:
const userInfo: any = undefined;
function catchError(msg: string) {
return function(target: any, key: string, descriptor: PropertyDescriptor) {
const fn = descriptor.value;
descriptor.value = function() {
try {
fn();
} catch (e) {
console.log(msg);
}
};
};
}
class Test {
@catchError('userInfo.name 不存在')
getName() {
return userInfo.name;
}
@catchError('userInfo.age 不存在')
getAge() {
return userInfo.age;
}
@catchError('userInfo.gender 不存在')
getGender() {
return userInfo.gender;
}
}
const test = new Test();
test.getName();
test.getAge();
- 属性装饰器
// 修改的并不是实例上的 name, 而是原型上的 name
function nameDecorator(target: any, key: string): any {
target[key] = 'lee';
}
// name 放在实例上
class Test {
@nameDecorator
name = 'Dell';
}
const test = new Test();
console.log((test as any).__proto__.name);
- 参数装饰器
// 原型,方法名,参数所在的位置
function paramDecorator(target: any, method: string, paramIndex: number) {
console.log(target, method, paramIndex);
}
class Test {
getInfo(name: string, @paramDecorator age: number) {
console.log(name, age);
}
}
const test = new Test();
test.getInfo('Dell', 30);