TypeScript入门篇
一、什么是 TypeScript?
- TypeScript 是添加了类型系统的 JavaScript,适用于任何规模的项目。
- TypeScript 是一门静态类型、弱类型的语言。
- TypeScript 是完全兼容 JavaScript 的,它不会修改 JavaScript 运行时的特性。
- TypeScript 可以编译为 JavaScript,然后运行在浏览器、Node.js 等任何能运行 JavaScript 的环境中。
- TypeScript 拥有很多编译选项,类型检查的严格程度由你决定。
- TypeScript 可以和 JavaScript 共存,这意味着 JavaScript 项目能够渐进式的迁移到 TypeScript。
- TypeScript 增强了编辑器(IDE)的功能,提供了代码补全、接口提示、跳转到定义、代码重构等能力。
- TypeScript 拥有活跃的社区,大多数常用的第三方库都提供了类型声明。
- TypeScript 与标准同步发展,符合最新的 ECMAScript 标准(stage 3)。
二、为什么用TypeScript?
TypeScript是JavaScript的一个超集,扩展了JavaScript语法,也就是说TypeScript包含了JavaScript
TypeScript = JavaScript + 类型约束 + 高级特性
三、安装TypeScript
1、使用npm(Node.js包管理器)
npm install -g typescript
2、visual studio code
安装VScode的TypeScript插件
四、代码转译
TypeScript
文件后缀使用 .ts
扩展名文件,可通过 tsc index.ts
进行转译,输出一个 index.js
文件,因为浏览器只解析HTML + CSS + JavaScript,所以使用时必须提前把 TS
代码转换成 JavaScript
代码,编译
和 转译
的微小差别在于:
- 编译是把源码转变成另一种语言
- 转译是把源码转变另一个相同抽象层级的语言
— 基础部分
原始数据类型
基本数据类型:
字符串类型 (string)
数字类型 (number)
布尔值类型 (boolean)
未定义类型 (undefined)
空值 (null)
符号 (symbol ES6新增) — 符号类型是唯一的并且是不可修改的
大整数(bigInt,ES6 新增)— 使用 BigInt
可以安全地存储和操作大整数,即使这个数已经超出了JavaScript构造函数 Number 能够表示的安全整数范围。
引用数据类型:
对象类型 (object)
函数 (function)
数组 (array)
一、基本数据类型
// 字符串类型
let name: string = "yuyu";
// 数字类型
let age: number = 512;
// 布尔值类型
let show: boolean = false;
// 未定义类型
let a: undefined = undefined;
// 空值
let b: null = null;
// 符号
let sym: symbol = Symbol("yy");
// 对象类型
let obj: object = {a: 1};
二、复杂数据类型
1、Array 数组
// 数组:元素类型[]
let arr:string[] = ['1','2','3','4'];
// Array<元素类型>
let arr1:Array<number> = [1,2,3,4,5];
// 定义指定对象成员的数组
interface objectItf {
name:string;
age:number;
}
let arr2 : objectItf[] = [{
name:'芋芋',
age:18
}]
2、元组 tuple
// 元组 -- 对于内部不同类型的数组可以使用元组类型来定义
let tuple:[number,string];
tuple = [18,'芋芋']; // --- res
tuple = ['芋芋',18]; // --- err
— 元组类型只能表示一个已知元素数量和类型的数组,已指定长度;如果一个数组可能有多个元素或者多种类型,就使用 any[]
,非必要情况尽量不使用 any
!
3、null 和 undefined
null和undefined分别拥有各自的类型,默认情况下它们是所有类型的子类型,即可以赋值给任意的类型。
let str1:string = 'a';
str1 = null; // --- res
str1 = undefined; // --- res
let num:number = 1;
num = null;
num = undefined;
可以通过配置tsconfig中 strictNullChecks = true
标记,开启严格模式检查,这种情况下,null和undefined和其他类型是平等关系,只能赋值给any类型和它们各自的类型,还有undefined是可以复制给void类型的。(当为一个函数声明返回类型为void时,函数未显示return的情况下,默认返回的就是undefined)
—– 会有一种情形,比如搭建vite项目,script设置了lang=’ts’时,默认null和undefined不能进行赋值,需要手动把strictNullChecks = false的时候才可以将null和undefined进行赋值,当前,现实场景中,我们不会将null和undefined进行赋值,如果数据初始值需要,我们会通过联合类型将变量添加一个为null/undefined的类型用来赋值。
4、没有类型 void
void
表示没有任何类型,和其他类型是平等关系,不能直接赋值
let a:void;
let b:number = a; // --- err
声明一个void类型的变量意义不大,一般只有在函数没有返回值的时候去声明(比如子传父时,event没有返回值的时候可以声明)
当方法没有返回值的时候将得到undefined,但是我们需要定义成void类型,不能定义undefined类型,不然会报错,因为当函数类型为void时,函数为显示return的情况下,默认返回undefined
5、任意类型 any 和 未知类型 unknown
any
会跳过类型检测,所有值都可以赋值给 any
类型。不推荐类型声明使用 any
,除非实在没有办法的情况下暂时声明,如果都用any类型的话,那么TypeScript将失去它本身的意义,只是用了这个语言,并没有其他实质性的约束。
let a: any = 4;
a = "1"; // --- res
a = false; // --- res
unknown
和 any
一样,所有值都可以赋值给unknown类型。
let a: unknown = "hello";
a = 12; // --- res
a = false; // --- res
a = null; // --- res
unknown又有很多方面不同于any类型,任何类型的值都可以赋值给any,同时any类型的值也可以赋值给任何类型,但unknown只是顶层类型,任何类型都可以赋值给unknown,但unknown只能赋值给unknown和any类型,因为只有这两个是顶层类型。
let a: unknown = 1;
let b: any = a; // b: any
let c: any = 1;
let d: unknown = a; // d: unknown
let e: unknown = 1;
let f: number = a; // 不能将类型“unknown”分配给类型“number”。
不缩小类型就无法对unknown类型进行操作,使其起到了很强的预防性以及安全性,所以我们必须缩小类型,可以使用 typeof
、 类型断言
等
let a: unknown = 1;
// 直接分配
let b = a.toLocaleLowerCase(); // 类型“unknown”上不存在属性“toLocaleLowerCase”.
// typeof
if (typeof a === "string") {
let c = a.toLocaleLowerCase(); // --- res
}
// 类型断言
let d = (a as string).toLocaleLowerCase(); // --- res
any
和 unknown
的最大区别是, unknown 是 top type (任何类型都是它的 subtype) , 而 any 即是 top type, 又是 bottom type (它是任何类型的 subtype ) ,这导致 any 基本上就是放弃了任何类型检查。
6、永不存在 never
never类型表示永不存在的值的类型。
never类型是任何类型的子类型,可以赋值给任何类型,但是never类型本身没有子类型了,它只能赋值给它本身,及时any类型也不可以赋值给never。
let err: never;
let num: number = 1;
let nev: never;
let any: any;
num = err; // --- res
err = 1; // 不能将类型“number”分配给类型“never”。
err = "1"; // 不能将类型“string”分配给类型“never”。
err = any; // 不能将类型“any”分配给类型“never”。
err = nev;
值会永不存在的两种情况:
- 如果一个函数执行时抛出了异常,那么这个函数永远不存在返回值(因为抛出异常会直接中断程序运行,这使得程序运行不到返回值那一步,即具有不可达的终点,也就永不存在返回了);
- 函数中执行无限循环的代码(死循环),使得程序永远无法运行到函数返回值那一步,永不存在返回。
function err(msg: string): never {
// OK
throw new Error(msg);
}
// 死循环
function infiniteLoop (): never {
// OK
while (true) {}
}
never 类型运算
因为 never
类型为底部类型,所以任意类型与 never
交叉都得到 never
:
type T1 = number & never; // never
type T2 = string & never; // never
对于上面的运算结果,我们可以这样理解:若 type T = T1 & T2
,则 T
类型的值可以赋给 T1
或 T2
类型的变量。那么如果与 never
类型交叉,则 T
类型的值可以赋给一个 never
类型的变量,那 T
只能是 never
了。
而任意类型与 never
类型联合,则不会影响原有的类型:
type T3 = number | never; // number
type T4 = string | never; // string
同样,对于上面的运算结果,我们可以这样理解:若 type T = T3 | T4
,则 T3
或 T4
类型的值可以赋给 T
类型的变量。由于 never
类型是任何类型的子类型,也可以赋值给任何类型的变量,自然对联合类型不产生影响。
结论:使用 never 类型避免出现新增了联合类型没有对应的实现,目的就是写出类型绝对安全的代码。