千锋教育前端TypeScript入门视频教程(陆神顶配版TS入门教程)

一、TypeScript概念
TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。
简而言之,TypeScript是JavaScript的超集,具有可选的类型并可以编译为纯JavaScript。从技术上讲TypeScript就是具有静态类型的 JavaScript 。
二、TypeScript优缺点
优点
· 增强代码的可维护性,尤其在大型项目的时候效果显著
· 友好地在编辑器里提示错误,编译阶段就能检查类型发现大部分错误
· 支持最新的JavaScript新特特性
· 周边生态繁荣,vue3已全面支持 typescript
缺点
· 需要一定的学习成本
· 和一些插件库的兼容并不是特别完美,如以前在 vue2 项目里使用 typescript就并不是那么顺畅
· 增加前期开发的成本,毕竟你需要写更多的代码(但是便于后期的维护)
三、TypeScript安装
首先,我们可以新建一个空文件夹,用来学习 ts,例如我在文件夹下新建了个 helloworld.ts
npm install -g typescript // 全局安装 ts
不记得自己是否已经安装过 typescript 的,可以使用以下命令来验证:
tsc -v
如果出现版本,则说明已经安装成功
Version 4.6.3
生成 tsconfig.json 配置文件
tsc --init
执行命令后我们就可以看到生成了一个 tsconfig.json 文件,里面有一些配置信息,我们暂时先按下不表
在我们helloworld.ts文件中,随便写点什么
1. const s:string = "你好,TS";
2. console.log(s);
控制台执行 tsc helloworld.ts 命令,目录下生成了一个同名的 helloworld.js 文件,代码如下
1. var s = "你好,TS ";
2. console.log(s);
在控制台输入下面的命令开启了监听模式 实时监测app.ts 同步到app.js
tsc app.ts -w
通过tsc命令,发现我们的typescript代码被转换成了熟悉的js代码
我们接着执行
node helloworld.js
即可看到输出结果
四、TypeScript基础类型
js的数据类型
number 、string、 null 、 undefined、 boolean、 object、 function、 array
ts新增的数据类型
any任意类型; enum 枚举 联合类型 字面量类型 unknown void
Boolean 类型
声明一个变量flag,同时指定它的类型为boolean
const flag: boolean = true;
Number 类型
声明一个变量a,同时指定它的类型为number
const count: number = 10;
String 类型
声明一个变量str,同时指定它的类型为string
let name: string = "树哥";
Enum 枚举类型
枚举类型用于定义数值集合,使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。,如周一到周日,方位上下左右等
(1)普通枚举
1. enum Color {
2. RED,
3. PINK,
4. BLUE,
5. }
6.
7. const red: Color = Color.RED;
8. console.log(red); // 0
初始值默认为 0 其余的成员会会按顺序自动增长 可以理解为数组下标
(2)设置初始值
1. enum Color {
2. RED = 2,
3. PINK,
4. BLUE,
5. }
6. const pink: Color = Color.PINK;
7. console.log(pink); // 3
(3)字符串枚举
1. enum Color {
2. RED = "红色",
3. PINK = "粉色",
4. BLUE = "蓝色",
5. }
6.
7. const pink: Color = Color.PINK;
8. console.log(pink); // 粉色
(4)常量枚举
使用 const 关键字修饰的枚举,常量枚举与普通枚举的区别是,整个枚举会在编译阶段被删除 我们可以看下编译之后的效果
1. const enum Color {
2. RED,
3. PINK,
4. BLUE,
5. }
6.
7. const color: Color[] = [Color.RED, Color.PINK, Color.BLUE];
8. console.log(color); //[0, 1, 2]
9.
10.//编译之后的js如下:
11.var color = [0 /* RED */, 1 /* PINK */, 2 /* BLUE */];
12.// 可以看到我们的枚举并没有被编译成js代码 只是把color这个数组变量编译出来了
Array 类型
对数组类型的定义有两种方式:
1. const arr: number[] = [1,2,3];
2. const arr2: Array<number> = [1,2,3];
元组(tuple)类型
上面数组类型的方式,只能定义出内部全为同种类型的数组。对于内部不同类型的数组可以使用元组类型来定义
元组( Tuple )表示一个已知数量和类型的数组,可以理解为他是一种特殊的数组
元组,元组就是固定长度的数组 语法:[类型, 类型, 类型]
const tuple: [number, string] = [1, "zhangmazi"];
需要注意的是,元组类型只能表示一个已知元素数量和类型的数组,长度已指定,越界访问会提示错误。例如,一个数组中可能有多种类型,数量和类型都不确定,那就直接any[]。
undefined和null
默认情况下 null 和 undefined 是所有类型的子类型。 也就是说你可以把 null 和 undefined 赋值给其他类型。
let a: undefined = undefined;
let b: null = null;
let str: string = 'zhangmazi';
str = null; // 编译正确
str = undefined; // 编译正确
如果你在tsconfig.json指定了"strictNullChecks":true ,即开启严格模式后, null 和 undefined 只能赋值给 void 和它们各自的类型。 (这里感谢评论区指出) null 和 undefined 只能给它们自己的类型赋值
// 启用 --strictNullChecks
let x: number;
x = 1; // 编译正确
x = undefined; // 编译错误
x = null; // 编译错误
- undefined 可以给 void 赋值
let c:void = undefined // 编译正确
let d:void = null // 编译错误
any 类型
any会跳过类型检查器对值的检查,任何值都可以赋值给any类型 any类型就是js(建议不要用)
ts会有命名规范,不能命名为any
any 表示的是任意类型,一个变量设置类型为any后相当于对该变量关闭了TS的类型检测
使用TS时,不建议使用any类型
声明变量如果不指定类型,则TS解析器会自动判断变量的类型为any (隐式的any)
any是可以赋值任何类型
let value: any = 1;
value = "zhangmazi"; // 编译正确
value = []; // 编译正确
value = {};// 编译正确
void 类型
void 意思就是无效的, 一般只用在函数上,告诉别人这个函数没有返回值。
function sayHello(): void {
console.log("hello 啊,树哥!");
}
never 类型
抛出异常
never 类型表示的是那些永不存在的值的类型。 例如never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型
值会永不存在的两种情况:
· 1 如果一个函数执行时抛出了异常,那么这个函数永远不存在返回值(因为抛出异常会直接中断程序运行,这使得程序运行不到返回值那一步,即具有不可达的终点,也就永不存在返回了)
· 2 函数中执行无限循环的代码(死循环),使得程序永远无法运行到函数返回值那一步,永不存在返回。
// 异常
function error(msg: string): never { // 编译正确
throw new Error(msg);
}
// 死循环
function loopForever(): never { // 编译正确
while (true) { };
}
Unknown 类型
unknown与any一样,所有类型都可以分配给unknown:
unknown 表示未知类型的值
只能赋值给unkonwn any
let value: unknown = 1;
value = "zhangmazi"; // 编译正确
value = false; // 编译正确
unknown与any的最大区别是:
任何类型的值可以赋值给any,同时any类型的值也可以赋值给任何类型。unknown 任何类型的值都可以赋值给它,但它只能赋值给unknown和any
五、TypeScript对象类型
这里所说的对象类型,就是我们常说的函数、{}、数组、类
object, Object 和 {} 类型
(1)object
object 类型用于表示所有的非原始类型,即我们不能把 number、string、boolean、symbol等 原始类型赋值给 object。在严格模式下,null 和 undefined 类型也不能赋给 object。
let object: object;
object = 1; // 报错
object = "a"; // 报错
object = true; // 报错
object = null; // 报错
object = undefined; // 报错
object = {}; // 编译正确
(2)Object
大 Object 代表所有拥有 toString、hasOwnProperty 方法的类型 所以所有原始类型、非原始类型都可以赋给 Object(严格模式下 null 和 undefined 不可以)
let bigObject: Object;
object = 1; // 编译正确
object = "a"; // 编译正确
object = true; // 编译正确
object = null; // 报错
ObjectCase = undefined; // 报错
ObjectCase = {}; // ok
(3){}
{} 空对象类型和大 Object 一样 也是表示原始类型和非原始类型的集合
类
在 TypeScript 中,我们通过 Class 关键字来定义一个类
//类
// class 类名 {
// 属性名: 类型;
// constructor(参数:类型){
// this. 属性名 = 参数
// }
// 方法名(){
// }
// }
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHi(): void {
console.log(`Hi, ${this.name}`);
}
}
数组
const flag1: number[] = [1, 2, 3];
const flag2: Array<number> = [1, 2, 3];
函数
函数声明
function add(x: number, y: number): number {
return x + y;
}
函数表达式
const add = function(x: number, y: number): number {
return x + y;
}
接口定义函数
interface Add {
(x: number, y: number): number;
}
可选参数
function add(x: number, y?: number): number {
return y ? x + y : x;
}
默认参数
function add(x: number, y: number = 0): number {
return x + y;
}
剩余参数
function add(...numbers: number[]): number {
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
函数重载
函数重载或方法重载是使用相同名称和不同参数数量或类型创建多个方法的一种能力。
function add(x: number, y: number): number;
function add(x: string, y: string): string;
function add(x: any, y: any): any {
return x + y;
}
上面示例中,我们给同一个函数提供多个函数类型定义,从而实现函数的重载
需要注意的是:
函数重载真正执行的是同名函数最后定义的函数体 在最后一个函数体定义之前全都属于函数类型定义 不能写具体的函数实现方法 只能定义类型
六、类型推论
如果没有明确的指定类型,那么 TypeScript 会依照类型推论的规则推断出一个类型。
let x = 1;
x = true; // 报错
上面的代码等价于
let x: number = 1;
x = true; // 报错
通过上述示例我们可以看出,我们没有给 x 指定明确类型的时候,typescript 会推断出 x 的类型是 number。
而如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查:
let x;
x = 1; // 编译正确
x = true; // 编译正确
七、类型断言
某些情况下,我们可能比typescript更加清楚的知道某个变量的类型,所以我们可能希望手动指定一个值的类型
类型断言有两种写法
(1)尖括号写法
let str: any = "to be or not to be";
let strLength: number = (<string>str).length;
(2)as 写法
let str: any = "to be or not to be";
let strLength: number = (str as string).length;
1.非空断言
在上下文中当类型检查器无法断定类型时,可以使用缀表达式操作符 ! 进行断言操作对象是非 null 和非 undefined 的类型,即x!的值不会为 null 或 undefined
let user: string | null | undefined;
console.log(user!.toUpperCase()); // 编译正确
console.log(user.toUpperCase()); // 错误
2.确定赋值断言
let value:number
console.log(value); // Variable 'value' is used before being assigned.
我们定义了变量, 没有赋值就使用,则会报错
通过 let x!: number; 确定赋值断言,TypeScript 编译器就会知道该属性会被明确地赋值。
let value!:number
console.log(value); // undefined 编译正确
八、联合类型
联合类型用|分隔,表示取值可以为多种类型中的一种
可以使用 | 来连接多个类型(联合类型)
let status:string|number
status='to be or not to be'
status=1
九、类型别名
类型别名用来给一个类型起个新名字。它只是起了一个新名字,并没有创建新类型。类型别名常用于联合类型。
type count = number | number[];
function hello(value: count) {}
十、交叉类型
交叉类型就是跟联合类型相反,用&操作符表示,交叉类型就是两个类型必须存在
interface IpersonA{
name: string,
age: number
}
interface IpersonB {
name: string,
gender: string
}
let person: IpersonA & IpersonB = {
name: "师爷",
age: 18,
gender: "男"
};
person 即是 IpersonA 类型,又是 IpersonB 类型
注意:交叉类型取的多个类型的并集,但是如果key相同但是类型不同,则该key为never类型
interface IpersonA {
name: string
}
interface IpersonB {
name: number
}
function testAndFn(params: IpersonA & IpersonB) {
console.log(params)
}
testAndFn({name: "黄老爷"}) // error TS2322: Type 'string' is not assignable to type 'never'.
十一、类型守卫
类型保护是可执行运行时检查的一种表达式,用于确保该类型在一定的范围内。 换句话说,类型保护可以保证一个字符串是一个字符串,尽管它的值也可以是一个数值。类型保护与特性检测并不是完全不同,其主要思想是尝试检测属性、方法或原型,以确定如何处理值。
换句话说:类型守卫是运行时检查,确保一个值在所要类型的范围内
目前主要有四种的方式来实现类型保护:
(1)in 关键字
interface InObj1 {
a: number,
x: string
}
interface InObj2 {
a: number,
y: string
}
function isIn(arg: InObj1 | InObj2) {
// x 在 arg 打印 x
if ('x' in arg) console.log('x')
// y 在 arg 打印 y
if ('y' in arg) console.log('y')
}
isIn({a:1, x:'xxx'});
isIn({a:1, y:'yyy'});
(2)typeof 关键字
function isTypeof( val: string | number) {
if (typeof val === "number") return 'number'
if (typeof val === "string") return 'string'
return '啥也不是'
}
typeof 只支持:typeof 'x' === 'typeName' 和 typeof 'x' !== 'typeName',x 必须是 'number', 'string', 'boolean', 'symbol'。
(3)instanceof
function creatDate(date: Date | string){
console.log(date)
if(date instanceof Date){
date.getDate()
}else {
return new Date(date)
}
}
(4)自定义类型保护的类型谓词
function isNumber(num: any): num is number {
return typeof num === 'number';
}
function isString(str: any): str is string{
return typeof str=== 'string';
}
十二、接口
我们使用接口来定义对象的类型。接口是对象的状态(属性)和行为(方法)的抽象(描述)
简单理解就是:为我们的代码提供一种约定
我们使用关键字interface来声明接口
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25
};
我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是 Person。这样,我们就约束了 tom 的形状必须和接口 Person 一致。
接口一般首字母大写。(当然挺多人也习惯 I 大写字母开头,用来表示这是一个接口)
设置接口可选|只读
interface Person {
readonly name: string;
age?: number;
}
· 可选属性,我们最常见的使用情况是,不确定这个参数是否会传,或者存在。
· 只读属性用于限制只能在对象刚刚创建的时候修改其值。此外 TypeScript 还提供了 ReadonlyArray 类型,它与 Array 相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改。
索引签名
有时候我们希望一个接口中除了包含必选和可选属性之外,还允许有其他的任意属性,这时我们可以使用 索引签名 的形式来满足上述要求。
需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
interface Person {
name: string;
age?: number;
[prop: string]: any; // prop字段必须是 string类型 or number类型。 值是any类型,也就是任意的
}
const p1:Person = { name: "张麻子" };
const p2:Person = { name: "树哥", age: 28 };
const p3:Person = { name: "汤师爷", sex: 1 }
我们规定 以 string 类型的值来索引,索引到的是一个 any 类型的值
接口与类型别名的区别
实际上,在大多数的情况下使用接口类型和类型别名的效果等价,但是在某些特定的场景下这两者还是存在很大区别。
TypeScript 的核心原则之一是对值所具有的结构进行类型检查。 而接口的作用就是为这些类型命名和为你的代码或第三方代码定义数据模型。
type(类型别名)会给一个类型起个新名字。 type 有时和 interface 很像,但是可以作用于原始值(基本类型),联合类型,元组以及其它任何你需要手写的类型。起别名不会新建一个类型 - 它创建了一个新名字来引用那个类型。给基本类型起别名通常没什么用,尽管可以做为文档的一种形式使用。
接口和类型别名都可以用来描述对象或函数的类型,只是语法不同
type MyTYpe = {
name: string;
say(): void;
}
interface MyInterface {
name: string;
say(): void;
}
都允许扩展
· interface 用 extends 来实现扩展
interface MyInterface {
name: string;
say(): void;
}
interface MyInterface2 extends MyInterface {
sex: string;
}
let person:MyInterface2 = {
name:'树哥',
sex:'男',
say(): void {
console.log("hello 啊,树哥!");
}
}
· type 使用 & 实现扩展
type MyType = {
name:string;
say(): void;
}
type MyType2 = MyType & {
sex:string;
}
let value: MyType2 = {
name:'树哥',
sex:'男',
say(): void {
console.log("hello 啊,树哥!");
}
}
不同点
· type可以声明基本数据类型别名/联合类型/元组等,而interface不行
// 基本类型别名
type UserName = string;
type UserName = string | number;
// 联合类型
type Animal = Pig | Dog | Cat;
type List = [string, boolean, number];
· interface能够合并声明,而type不行
interface Person {
name: string
}
interface Person {
age: number
}
// 此时Person同时具有name和age属性