TypeScript 小结
TypeScript 是什么?
TypeScript 是由微软开发的一种自由和开源的编程语言。它是 JavaScript 的一个超集,本质上是在 JavaScript 的基础上添加了可选的静态类型和基于类的面向对象编程。
TypeScript 和 JavaScript 的区别?
TypeScript 的安装
# npm安装
npm install -g typescript
# 验证
tsc -v # Version 4.6.4
# 编译
tsc helloworld.ts # helloworld.ts => helloworld.js
TypeScript 的数据类型
- Boolean 类型
- Number 类型
- String 类型
- Symbol 类型
- Array 类型
- Enum 类型
- 数字枚举
enum Direction { NORTH = 3, // 默认初始值从0开始, 可手动指定 SOUTH, EAST, WEST, }
- 字符串枚举
enum Direction { NORTH = "NORTH", SOUTH = "SOUTH", EAST = "EAST", WEST = "WEST", }
- 常量枚举
const enum Direction { NORTH, SOUTH, EAST, WEST, }
- 异构枚举
enum Enum { A, B, C = "C", D = "D", E = 8, F, }
- Any 类型
- 在 TypeScript 中,任何类型都可以归类为 any 类型,本质上 any 类型是类型系统的顶级类型(全局超级类型)。TypeScript 允许开发者对 any 类型的值执行任何操作,而无需事先执行任何形式的检查
- 使用 any 类型,可以很容易地编写出类型正确但在实际代码运行过程中有问题的代码,使用了 any 类型就无法使用 TypeScript 提供的大量的保护机制
- Unknown 类型
- 为了解决 any 类型带来的问题,TypeScript 3.0 引入了 unknown 类型
- 就像所有的类型都可以赋值给 any 类型一样,所有的类型也都可以赋值给 unknown 类型。这样使得 unknown 类型成为 TypeScript 类型系统的另一种顶级类型
- unknown 类型只能被赋值给 any 类型和 unknown 类型本身
- Tuple 类型
- 元组是TypeScript 中特有的类型,其工作方式类似于数组,元组可用于定义具有有限数量的未命名属性的类型,每个属性都有一个关联的类型。使用元组时,必须提供每个属性的值
let tupleType: [ string, boolean ]; tupleType = [ "semlinker", true ];
- Void 类型
- void 类型与 any 类型相反,它表示没有任何类型,当一个函数没有返回值时,可以设置返回值类型为 void
- Null 和 Undefined 类型
- object,Object 和 {} 类型
- Never 类型
- never 类型表示那些永远不存在的值的类型(例如那些总是会抛出异常或者根本不会有任何返回值的函数表达式或箭头函数表达式)
TypeScript 的断言
- 类型断言
- 类型断言类似于类型转换,但是不进行特殊的数据检查和解构,它没有运行时的影响,只是在编译阶段起作用
const someValue: any = "this is a string"; const strLength: number = (<string>someValue).length; // 尖括号写法 const strLength: number = (someValue as string).length; // as写法
- 非空断言
- 在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符 ! 可以用于断言操作对象是非 null 和非 undefined 类型。具体而言,x! 将从 x 值域中排除 null 和 undefined
function myFunc(maybeString: string | undefined | null) { // Type 'string | null | undefined' is not assignable to type 'string'. // Type 'undefined' is not assignable to type 'string'. const onlyString: string = maybeString; // Error const ignoreUndefinedAndNull: string = maybeString!; // Ok }
TypeScript 的接口
interface Person {
name: string;
age?: number; // 可选属性
readonly sex: number; // 只读属性
[ x: string ]: any; // 任意属性
}
let semlinker: Person = {
name: "semlinker",
age: 33,
};
TypeScript 接口与类型别名的区别?
- 接口和类型别名都可以用来描述对象的形状或函数签名
// 接口 interface Point { x: number; y: number; } interface SetPoint { (x: number, y: number): void; } // 类型别名 type Point = { x: number; y: number; }; type SetPoint = (x: number, y: number) => void;
- 与接口类型相比,类型别名可用于一些其他类型(比如原始类型、联合类型和元组)
// primitive type Name = string; // object type PartialPointX = { x: number; }; type PartialPointY = { y: number; }; // union type PartialPoint = PartialPointX | PartialPointY; // tuple type Data = [ number, string ];
- 扩展
// Interface extends interface interface PartialPointX { x: number; } interface Point extends PartialPointX { y: number; } // Type alias extends type alias type PartialPointX = { x: number; }; type Point = PartialPointX & { y: number; }; // Interface extends type alias type PartialPointX = { x: number; }; interface Point extends PartialPointX { y: number; } // Type alias extends interface interface PartialPointX { x: number; } type Point = PartialPointX & { y: number; }; // 与类型别名不同, 接口可以定义多次, 多个接口会被自动合并为单个接口 interface Point { x: number; } interface Point { y: number; } const point: Point = { x: 1, y: 2 };
TypeScript 的泛型
function identity <T, U>(value: T, message: U) : T {
console.log(message);
return value;
}
identity<Number, string>(68, "Semlinker");
// 我们也可以完全省略尖括号, 让编译器自动选择这些类型
identity(68, "Semlinker")
// 泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
// 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
return x + y;
};
// 泛型工具类型
// Partial<T>的作用就是将某个类型里的属性全部变为可选项?
// 首先通过keyof T拿到T的所有属性名, 然后使用in进行遍历, 将值赋给P
// 最后通过T[P]取得相应的属性值, 中间的?号用于将所有属性变成可选
type Partial<T> = {
[ P in keyof T ]?: T[ P ];
};
TypeScript 的配置文件
- tsconfig.json 的作用
- 用于标识 TypeScript 项目的根路径
- 用于配置 TypeScript 编译器
- 用于指定编译的文件
- tsconfig.json 的重要字段
- files:设置要编译的文件的名称
- include:设置需要进行编译的文件,支持路径模式匹配
- exclude:设置无需进行编译的文件,支持路径模式匹配
- compilerOptions:设置与编译流程相关的选项
- compilerOptions 的选项
{ "compilerOptions": { /* 基本选项 */ "target": "es5", // 指定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 // 为装饰器提供元数据的支持 } }
TypeScript 的 .d.ts
.d.ts 文件是 TypeScript 用来声明变量、模块、type、interface 等等的。
和纯 js 文件相比,在 .d.ts 文件中声明变量或者模块后,在其他地方可以不用 import 导入就能直接使用,并且还有语法提示。
但并不是说创建了 .d.ts 文件,里面声明的东西就能生效,毕竟归根到底它也是 .ts 文件,需要进行预编译,所以需要在 tsconfig.json 文件里面的 include 数组里面添加这个文件。
include 数组里面可以不用写 .d.ts 文件的绝对路径,可以通过 glob 通配符,匹配这个文件所在的文件夹或者是相对路径。
TypeScript 的 declare
.d.ts 文件中的顶级声明必须以 declare 或者 export 修饰符开头。
通过 declare 声明的类型、变量和模块,在 include 包含的文件范围内,都可以直接引用而不用去 import 或者 import type 相应的变量或类型。
// declare声明一个类型
// 在include包含的文件范围内可以直接使用这个type
declare type Asd {
name: string;
}
// declare声明一个模块
// 在编辑ts文件的时候, 如果你想导入一个.css/.less/.png格式的文件, 如果没有经过declare是会提示语法错误的
declare module '*.css';
declare module '*.less';
declare module '*.png';
// declare声明一个变量
// 假如在项目中引入了一个sdk(以微信的sdk为例), 里面有一些全局的对象(比如wx), 但是如果不经过任何的声明, 在ts文件里面直接用wx.config(), 肯定会报错的
// declare就是告诉ts编译器, 你担保这些变量和模块存在, 同时声明了相应类型, 在编译的时候不需要提示错误
// declare声明一个作用域
// 声明完之后在其他地方的ts就可以直接通过API.ResponseList引用到这个接口类型
declare namespace API {
interface ResponseList {}
}
- .d.ts 文件的顶级声明 declare 最好不要和 export 同级使用,不然在其他 ts 文件中引用这个 .d.ts,就需要手动 import 导入了
- 在 .d.ts 文件里,如果顶级声明不用 export,declare 和直接写 type、interface 的效果是一样的,在其他任何地方都可以直接引用
参考文档
- https://juejin.cn/post/6872111128135073806#heading-0
- https://juejin.cn/post/7083869402001178655
- https://www.tslang.cn/docs/handbook/tsconfig-json.html