TypeScript 是什么
简单来说TS是一种由微软开发的编程语言,其为JS的一个超集,且本质上向这个语言添加了可选的静态类型与基于类的面向对象编程,规范化语言的同时对JS进行了补强
TS与JS的区别
层面 | TypeScript | JavaScript |
---|---|---|
语言层面 | TS为JS的超集,用于解决大型项目的代码复杂性 | 一种脚本类语言 |
类型层面 | 强类型,支持静态与动态类型 | 弱类型,没有静态类型 |
执行层面 | 最终会被编译成JS代码才能执行,编译期间发现并纠正错误 | 直接在浏览器中使用 |
支持层面 | 支持模块、泛型以及接口,代码更易维护 | 不支持模块、泛型以及接口 |
优点
- 增强了代码的可读性与可维护性,编程者可通过定制好的接口快速判断当前数据内容
- 增强了代码的健壮性,TS为强制类型语言,在遇到类型错误时会及时报错并注解错误内容,节约试错成本
- 对JS有良好的包容性,即使在TS文件中不使用TS语法也不会有问题
- 对JS的类型进行了进一步的补强,使开发更为安全,项目数据逻辑更为清晰
缺点
- 有一定的学习成本,需要理解接口、泛型、类、枚举类型等概念
- 由于需要多写一些类型的定义,短期会增加部分开发成本
- 与部分库或是技术结合的不是很完美(Vue3之前对TS支持不完善)
安装与使用 TypeScript
安装TS
npm i -g typescript // 全局安装 TS
tsc -v // 查看 TS 版本
复制代码
注:推荐用vscode搭配相关TS插件使用
使用TS
项目初始化
新建文件夹并打开控制台输入 tsc –init 生成 tsconfig.json 文件
TS执行相关
- 控制台使用 tsc 文件名 可生成对应 JS 文件
- 控制台直接输入 tsc 则会默认执行 tsconfig 文件中的内容
- 若是需要生成特定TS文件的JS代码则需在 tsconfig 文件首部 “include”:[“文件名1.ts” , “文件名2.ts”],
- 不想让指定文件编译可在文件首部 “exclude”:[“文件1.ts” , “文件2.ts”],
- tsconfig中的 “files” 与include简单使用是一致的,但是files不会被exclude排除,include可以写正则且会被exclude排除
tsconfig.json重要条目(逐步更新)
{
//修改后需重启项目
"include": ["tsconfig文件.ts"], // 列表中文件会编译为js
"exclude": [], // 列表中文件不编译为js
"compilerOptions": {
"sourceMap": true, // 开启后编译时生成 sourceMap
"outDir": "./build", // 生成js所存放的目录
"rootDir": "./", // 入口文件,编译器会在该目录下寻找ts文件
"removeComments": true, // 编译去除注
"strict": true, // 编译与书写规范要严格按照ts规范
"noImplicitAny": true, // 是否允许你的注解类型any不用特意标明,改为false时不强制要求
"strictNullChecks": true, // 是否允许ts文件中有 null 出现,false 为允许
"noUnusedLocals": true, // 对未使用的变量进行提示
"noUnusedParameters": true, // 对未使用的方法进行提示
}
}
复制代码
TypeScript 基础类型
Boolean 类型
该类型为简单的 true / false 值
let isDone: boolean = false; // 定义一个初始为 false 的 boolean 变量
复制代码
Number 类型
该类型为默认为浮点数的数字,其支持二进制、八进制、十进制与十六进制
let num: number = 6; // 定义一个初始为6的浮点变量
复制代码
String 类型
可使用( “” )/( ” )/( “ )定义一条字符串,并且可以使用 ${ expr } 嵌入表达式
let str: string = `name:${userName}`; // 定义一条字符串,其值为 name:+ 变量值
复制代码
Array 类型
let list: number[] = {1, 2, 3}; // 定义一个使用 number 类型元素组成的数组
let list: Array<number> = {1, 2, 3}; // 使用数组泛型定义一个数组
复制代码
Enum 类型
该类型是对JS标准数据类型的一个补充,使用枚举类型可为一组数值赋予友好的名字
数字枚举
这种情况默认会从0开始为元素进行递推编号,也可以手动执行成员数值
enum Color {Red = 1, Green, Blue}; // 定义一个数字枚举类型的 Color ,其编号从1开始递推
let colorName: string = Color[2]; // 此处 Color 值为 Green
复制代码
字符串枚举
在 TS2.4 版本以上允许使用字符串枚举,在单个字符串枚举中每个成员都需使用字符串字面量或另一个字符串枚举成员进行初始化
enum Color {Red = "RED", Green = "GREEN", Blue = "BLUE"}; //定义一个字符串枚举类型的 Color
let colorName: string = Color["Green"]; //此处 Color 值为 GREE
复制代码
Any 类型
该类型可代表任意类型,其为类型系统的顶级类型(全局超集类型)
let test: any = "wuhu";
test = 111; // ok
test = true; // ok
复制代码
Unknown 类型
类似于 any ,所有类型都可以赋值给该类型,但是该类型不能赋值给已有非 any 或 unknown 类型的变量
let value: unknown;
let value1: any = value; // ok
let value2: unknown = value; // ok
let value3: number = value; // Error
let value4: string = value; // Error
复制代码
Tuple 类型
该类型表示一个已知元素数量与类型的数组,各元素的类型不必相同
let x: [string, number]; // 定义一个第一位为 string 类型,第二位为 number 类型的元组
x = ['hello', 10]; // 正确赋值
x = [10, 'hello']; // 类型位没有一一对应,错误赋值
复制代码
当访问一个已知索引的元素会得到其正确类型,若该类型上并没有调用方法则会报错
x[0].substr(1); // ok
x[1].substr(1); // Error, 'number' does not have 'substr'
复制代码
当访问一个越界元素会使用联合类型替代(在此例中就是 string | number )
x[3] = "world"; // ok 字符串可以赋值给 string | number
x[6] = true; // Error 布尔不属于该联合类型
复制代码
Void 类型
该类型表示没有任何类型,通常用于处理没有返回值的函数
function test(): void{}; // 声明一个没有返回值的函数
复制代码
Null 与 Undefined 类型
这两种类型为所有类型的子级,非严格模式下可赋值给其他类型变量,严格模式则只能赋值给 void 或是他们各自类型
Never 类型
该类型用于表示永不存在的值,可用于指定根本不会有返回值的函数返回值
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
复制代码
其可用于收窄的兜底检测类型
interface Foo {
type: 'foo'
}
interface Bar {
type: 'bar'
}
type All = Foo | Bar // 若日后修改了此处的类型而没有增加类型 case 判断则会赋值给 never 类型变量导致报错
function handleValue(val: All) {
switch (val.type) {
case 'foo':
// 这里 val 被收窄为 Foo
break
case 'bar':
// val 在这里是 Bar
break
default:
// val 在这里是 never
const exhaustiveCheck: never = val // 此处用于兜底验证,确保代码的健壮性
break
}
}
复制代码
在 TS3.7 之后返回 never 的函数会被纳入控制流分析,其之后的代码会被判定为 unreachable ,可用于 unreachable code 分析
TypeScript 断言
但编程者比TS更了解某个值的详情信息时可使用类型断言强制为元素定义类型
尖括号
let str: any = "str"
let len: number = (<string>str).length;
复制代码
as 语法
let str: any = "str"
let len: number = (str as string).length;
复制代码
类型守卫
类型守护为可执行时用于确保类型在一定范围内的表达式
其主要思想为尝试检测属性、方法或原型以确定如何处理值
in 关键字
通过判断是否存在决定执行逻辑
interface type1{
firstName: string;
}
interface type2{
lastName: string;
}
type totalName = type1 | type2;
function dosth(name: totalName){
if("firstName" in name){ ... } // 通过 in 判断是否存在该属性名
if("lastName" in name){ ... }
}
复制代码
typeof 关键字
通过类型判断决定执行逻辑
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") { ... } // 通过 typeof 判断类型
if (typeof padding === "string") { ... }
}
复制代码
instanceof 关键字
通过原型链判断决定执行逻辑
interface test{
dosth: string;
}
class testClass implements test{
constructor(param: string){
this.str: string = param;
}
dosth(): string{
return this.str;
}
}
let tester: test = new testClass('wuhu')
if(tester instanceof test){ ... } // 检测test是否处于tester的原型链上
复制代码
联合类型与类型别名
联合类型
联合类型可以理解为逻辑中的“ 或“ ,其通常与 null 或 undefined 一起使用
let name: string | undefined; // 可接收 string 类型或是 undefined 类型,下同
const fun = (name: string | undefined) => { ... }
复制代码
类型别名
类型别名用于给一个类型或多个类型联合起一个新名字
type Massage = string | string[]; // 将 string 与 string[] 联合命名为 Message
let greet = (message: Message) => { ... }; // 使用时按照正常类型使用即可
复制代码
可辨识联合
该类型也被称为代数数据类型或是标签联合类型,其包含三个要点
- 可辨识:要求联合类型中的每一个成员都有一个相同的单例类型属性
- 联合类型:主类型中可包含多个子类型,各子类型间为 ”或“ 关系
- 类型守卫:用于区分各子类型并创建相应的逻辑空间
interface test1 {
vType: "test1"; // 两接口都拥有相同的可辨识属性 vtype
...
}
interface test2 {
vType: "test2";
...
}
type totalType = test1 | test2; // 此处创建联合类型 totalType
function func(arg: totalType){
switch(arg.vtype){ // 此处使用类型守卫对逻辑区块进行区分
case "test1": { ... }; // 执行相应区块逻辑
case "test2": { ... };
}
}
const myTest: test1 = { vType: "test1", ... };
func(myTest);
复制代码
交叉类型
该类型可将多个类型合并为一个类型,其包含所需所有类型的特性(类似于逻辑中的 ”与“ )
interface test1{
value1: string;
value2: string;
}
interface test2{
value3: string;
value4: string;
}
const totalTest = test1 & test2 // 此时 totalTest 拥有 test1 与 test2 的所有特性
复制代码
TypeScript 函数
TypeScript 函数与 JavaScript 函数区别
TypeScript | JavaScript |
---|---|
含有类型 | 无类型 |
函数类型 | 非函数类型 |
必填和可选参数 | 所有参数可选 |
可函数重载 | 无函数重载 |
TypeScript 函数详解
构建语法
普通函数写法
// 创建函数名为 test 的普通函数并传入 string 类型与 number 类型参数,函数最终返回 string 类型结果
let ans = function(arg1: string, arg2: number): string{ ... }
复制代码
箭头函数写法
let ans = (arg1:string, arg2:number): string => { ... } // 构建函数并将结果赋予 ans
复制代码
函数类型
定义方式
// 此处定义名为 template ,传入 string 与 number 类型最终返回 string 类型结果的函数类型
let template: (arg1: string, arg2: number): string;
复制代码
可选参数与默认参数
function test(arg1: string, arg2?: number):string { ... } // 参数二即为可选参数
// 注:由于函数是按顺序获取参数,可选参数一定要置于必填项后,否则影响传参顺序
function test(arg1: string = "str", arg2: number):string { ... }
复制代码
剩余参数
function test(arg1:string, ...args): string{ ... } // args 即为剩余参数数组
复制代码
函数重载
- 函数重载或方法重载是使用相同名称和不同参数数量或类型创建多个方法的能力
- 实现方法是为同一函数提供多个函数类型定义,编译器会根据这个列表处理函数的调用
// 函数重载
function add(a: number, b: number): number;
function add(a: string, b: string): string;
// 方法重载,此时要求同一类中方法名相同当时参数列表不同
class test{
add(a: number, b: number): number;
add(a: string, b: string): string;
}
复制代码
TS在处理函数重载时会查找重载列表,依次尝试定义,故一定要把最精确的定义放在最前面
to be continue~