TypeScript基础
# 1.前言
什么是typescript?
TypeScript简称TS TS和JS之间的关系其实就是Less/Sass和CSS之间的关系 就像Less/Sass是对CSS进行扩展一样, TS也是对JS进行扩展 就像Less/Sass最终会转换成CSS一样, 我们编写好的TS代码最终也会换成JS TypeScript是JavaScript的超集,因为它扩展了JavaScript,有JavaScript没有的东西。 硬要以父子类关系来说的话,TypeScript是JavaScript子类,继承的基础上去扩展。
为什么需要TypeScript?
简单来说就是因为JavaScript是弱类型, 很多错误只有在运行时才会被发现 而TypeScript提供了一套静态检测机制, 可以帮助我们在编译时就发现错误
TypeScript特点
支持最新的JavaScript新特特性 支持代码静态检查 支持诸如C,C++,Java,Go等后端语言中的特性 (枚举、泛型、类型转换、命名空间、声明文件、类、接口等)
# 2.搭建TypeScript 本地环境
- 全局安装typeScript
npm i -g typescript
- 安装ts-node
npm i -g ts-node
- 初始化一个项目
tsc --init
- 运行ts文件的编译命令[可以通过package.json 配置编译命令]
tsc index.ts
# 3 基本数据类型定义
# 3.1 常用基本数据类型
// 基本数据类型
let str:string="啦啦啦啦";
let num:number = 123;
let bool:boolean=true;
let nu:null = null;
let un:undefined = undefined;
console.log(str,num,bool,nu,un); //啦啦啦啦 123 true null undefined
2
3
4
5
6
7
8
# 3.2 ES6 新增基本数据类型
注意: 如果要使用此种数据类型需要将
tsconfig.json
中的lib
选项解注释并加入以下内容lib: ["ESNext"]
或 直接更改ts整体编译 target: ES2020"target": "es2020"
# 3.2.1 长整数 BigInt
const big: BigInt = BigInt(Number.MAX_SAFE_INTEGER);
console.log(big); //9007199254740991n
2
# 3.2.2 唯一值 Symbol
let id: symbol = Symbol(1);
let id1: symbol = Symbol(2);
console.log(id); //Symbol(1)
console.log(id1); //Symbol(2)
2
3
4
5
# 3 数组定义
let arr:string[]=['1','2','3','4'];
let arr1:Array<number> = [100,200,300];
let arr2:(string|number)[] = ['1',2,'3',4];
# 4 接口 定义 对象的类型 interface
# 4.1 基础使用
interface User{
name:string,
age:number,
}
let user1:User={name:"张麻子",age:40};
2
3
4
5
# 4.2 可以合并的 类型
interface User{
name:string,
age:number
}
interface User {
like:string,
}
let user:User = {name:'张麻子',age:22,like:'xxxxx'};
2
3
4
5
6
7
8
9
# 5 对象数组 定义
let list:(User)[] = [{name:"黄四郎",age:45},{name:"马邦德",age:44}]
let list:Array<User> = [{name:"黄四郎",age:45},{name:"马邦德",age:44}]
2
# 6.元组
和数组类似,但是其中的类型和顺序以及数量都需要被提前定义。
let arr:[string,number,string]= ['张麻子',22,'敲代码'];
//不可以通过索引新增元祖的元素
// arr[3]='啦啦啦';
// 可以通过push追加,这是ts 元祖的缺陷
arr.push('啦啦啦')
2
3
4
5
//长度可变元祖定义
let arr:[number,...any] = [100]
arr[1] = '你好啊'
2
3
# 6 函数定义
function sum(a:number,b:number):number{
return a+b;
}
console.log(sum(100,200));
2
3
4
// 可选参数类型
let func: (name: string, age?: number) => void;
// 可选参数一定要在必选参数的后面。
func = function (name: string, age?: number): void {
console.log(name, age);
};
func('zhangsan'); // zhangsan undefined
// 默认参数一定要在必选参数的后面。
func = function (name: string, age: number = 24): void {
console.log(name, age);
};
func('zhangsan'); // zhangsan 24
2
3
4
5
6
7
8
9
10
11
12
13
14
# 7 void 空类型
//函数没有返回值
const avg = (a:number,b:number,c:number):void=>{
console.log(a+b+c);
}
avg(100,200,300)
2
3
4
5
# 8 any 类型
定义任何变量 可以赋值为任何数据类型,写any 相当于没有类型限制
let num1:any=123;
num1='100';
console.log(num1);
2
3
# 9 类的定义 class
# 9.1 简单类的定义
class Person {
// public 表示实例公共属性
public name: string;
public age: number;
// public可以省略
like: string;
constructor(name: string, age: number, like: string) {
this.name = name;
this.age = age;
this.like = like
}
// 实例方法
public say(): string {
return '你好啊,我是' + this.name;
}
// 静态方法
static eat(food: string): string {
return '我正在吃' + food;
}
}
let p1 = new Person('张三', 22, '抽烟,喝酒,烫头');
console.log(p1);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 9.2 简单类省略写法
每次定义类 都需要在构造函数前面去定义每个属性的类型太过繁琐,可以直接简写到构造函数中
- 原写法
class Person {
// public 表示实例公共属性
public name: string;
public age: number;
// public可以省略
like: string;
constructor(name: string, age: number, like: string) {
this.name = name;
this.age = age;
this.like = like
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
- 简写 直接把定义属性的关键字放到构造函数中
class Person {
// 省略写法,不需要提前定义类型
// public name: string;
// public age: number;
// like: string;
constructor( public name: string, public age: number, public like: string) {
// 省略写法,不需要再去对每个属性赋值
// this.name = name;
// this.age = age;
// this.like = like
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
# 9.3 必传与可选参数
?: 可选参数,可以不传,默认类型为隐式any,默认值为undefined
!: 必传参数,必须传递
class Person {
// !: 必传参数
public name!: string;
// ?: 可选参数,默认类型 number | undefined ,默认值:undefined
public age?: number;
constructor(name: string, age?: number) {
this.name = name;
this.age = age;
}
}
let p1 = new Person('张三', 22); //Person { name: '张三', age: 22 }
let p2 = new Person('李四') //Person { name: '李四', age: undefined }
// let p3 = new Person(); //报错 必须传递1-2个参数
2
3
4
5
6
7
8
9
10
11
12
13
14
# 9.4 类的修饰符
类的属性和方法 有特定的作用或者限制 就需要通过修饰符进行处理
js中类的修饰符: public static readonly(只读)
ts中类的修饰符:除了js类的三个修饰符之外,新增了:
- public (公共) 公共属性、方法,可以在实例以及子类中访问
- protected (保护) 只能在子类中访问该属性和方法
- private(私有) 只能在当前类中访问该属性和方法
class Person {
protected age!: number;
private name!: string;
public sex!: string;
private getName() {}
protected getAge() {}
public getSex() {}
// name 和 getName() 只能在 Person 中用 this 访问
}
class User extends Person {
constructor() {
super();
// User中可以访问 getAge() 和 age 以及被 public 修饰的属性以及方法
}
}
const person = new Person(); // person 只能访问到 getSex() 和 sex
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
注意: private 私有属性与方法 只能在当前类中使用,不能通过子类 和 this实例访问
class Person{
private name:string;
constructor(name:string){
this.name = name;
}
private say(){
return 'ssss';
}
// 私有方法只能通过当前类进行使用
public xxx(){
console.log(this.name);
console.log(this.say())
}
}
let p1 = new Person('张麻子');
// p1.name = '黄四郎' // 私有属性 使用报错
// p1.say() //私有方法调用报错
p1.xxx() //通过实例正确的使用方法
console.log(p1); //Person { name: '张麻子' }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 9.5 类的存取器
如果想通过 实例对象直接访问或修改 类中的私有属性与方法,除了通过公有方法进行调用与操作之外,TS提供了存取器来进行操作
TS 支持通过
getter/setter
来截取对象成员的访问假设我们现在有个类,其中
name
是私有属性,但是我们又想要更改以及访问这个属性,此时就可以使用存取器Object.defineProperty 也拥有存取器
class Person{
private _name:string;
constructor(name:string){
this._name = name;
}
// 获取name时自动执行
get name():string{
return this._name;
}
// 修改name时 自动修改_name
set name(val:string){
this._name = val;
}
}
let p1 = new Person('张麻子');
console.log(p1.name) //自动执行 get 张麻子
p1.name = '黄四郎' //自动执行 set
console.log(p1.name) //黄四郎
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 10.枚举类型 enum
作用:一般用来定义 多种不同的状态
enum State{
Pending="PENDING",
Success="SUCCESS",
Fial="FIAL"
}
console.log(State.Pending) //PENDING
2
3
4
5
6
// 无默认值的枚举值 默认从 0 开始
enum State{
Pending,
Fial,
Success,
}
console.log(State.Pending) //0
console.log(State.Fial) //1
console.log(State.Success) //2
2
3
4
5
6
7
8
9
# 11.泛型
泛型: 泛指一切类型
泛型可以理解为 类型变量: 可变的类型
# 11.1 场景:
如果一个函数 传入了数字类型,就返回数字 ,如果传入了字符串,就返回一个字符串
function fn(a:number):number{
return a;
}
function fn(a:string):string{
return a;
}
2
3
4
5
6
- 泛型写法
function fn<T>(a:T):T{
return a;
}
fn(100) //100
fn('张麻子') //张麻子
fn(true) //true
//调用时传入泛型的类型
fn<number>(1000) //1000
fn<object>({name:'张麻子'}) //{name:'张麻子'}
2
3
4
5
6
7
8
9
# 11.2 理解:
# 11.3 泛型语法说明
为什么是 T
? 难道不能是其他的字母或者单词吗?当然可以 可以使任意单词或者字母,只要语义化清晰即可。建议首字母大写。当然 T
并不是毫无道理。
- T : 代表 Type 在定义泛型时通常用作第一个类型变量名称.
- K : 表示对象中的 key 类型。
- V : 表示对象中的 value 类型。
- E : 表示 Element 元素类型。
# 11.4 多个泛型参数
function fn<N,M>(a:N,b:M):N{
return a;
}
console.log( fn(1000,'sss') ); //1000
2
3
4
# 11.5 泛型 约束
使用泛型之后 会造成一些问题: 泛型之间不能使用运算符,泛型拿到数组、元祖之后没有length
解决length问题需要使用到 泛型约束
泛型约束:可以理解为 该泛型可以包含的属性及属性类型
这里出现
extends
关键字,此处表示泛型必须包含某种类型,从而在调用时约束了传入的类型。
function fn<T>(arr:T):number{
return arr.length; //报错 泛型T 不存在 length属性
}
console.log( fn([100,200,300]) );
2
3
4
5
- 泛型约束写法
// 定义泛型的约束
interface Len{
length:number
}
function fn<T extends Len>(arr:T):number{
return arr.length;
}
console.log( fn([100,200,300]) ); //3
2
3
4
5
6
7
8
- 多个泛型约束
如果需要约束多个条件
<T extends Type1, Type2, ...>
# 11.6 泛型问题
泛型之间不能使用运算符
解决方案: 函数重载
function add<T>(a:T,b:T):T{
return a+b //报错 两个泛型不能进行加法运算
}
2
3
- 函数重载
需要注意的是只有最后一个才是真正的函数,其他的地方只能是函数类型的定义。
function add(a: string, b: string): string
function add(a: number, b: number): number
function add(a: any, b: any) {
return a + b;
}
console.log(add(11,22)); //33
console.log(add('ssss','eeeee')); //sssseeeee
2
3
4
5
6
7
# 12 never 不存在的类型
抛出异常,或者永远不会有返回值的函数以及永远不会为真的类型返回
never
。
function infiniteLoop(): never {
while (true) {}
}
function error(): never {
throw '异常错误';
}
error()
infiniteLoop()
2
3
4
5
6
7
8
9
# 13 unknown 未知类型
unknown 和 any 很类似
定义为 unknown 类型的变量可以接收 任意数据类型
unknown 的变量只能赋值给 unknown 和any 类型的变量
any 定义的类型变量可以赋值给任意类型变量
let a:unknown;
a=1;
a='222'
console.log(a); //'222'
2
3
4
let a:unknown = '100';
// let b:string = a; //不能讲unknown 类型 分配给string类型
let c:unknown = a; //'100'
let d:any = a; //'100'
2
3
4
# 14 as 类型断言
在某种情况下我们可能明确的知道这个值的类型,但是静态检查不是想要的类型。此时可以使用类型断言确定这个值的类型
let arr = [111,'2222',333];
// let len = arr[1].length; //报错 很明显'2222'是一个字符串,但是却没有length属性
// 使用强制断言 可以强制确定该变量的类型
let len = (arr[1] as string).length; //4
2
3
4
5
6
# 15 联合类型
表示一个类型可以是多种类型中的一种
// 两个类型 选择一个
let a: string | number;
a = 1;
a = '1';
console.log(a) //'1'
2
3
4
5
# 16 type 类型别名
type 可以对 比较复杂的类型 取一个别名,简化 ts 的代码
type 和 interface 有很多相同之处 也有不同之处
相同:type 和interface 都可以定义 对象和 函数的类型
不同:
interface 是接口:用于描述一个对象 使用 extends 或implements 是 必须使用 interface
type 是类型别名: 使用组合或者交叉类型 必须使用type
# 16.1 type 定义类型别名
type Name = string //type 取别名
type NameResolver = () => string //type 取别名
type NameOrResolver = Name | NameResolver // 联合类型 通过type 取别名 简化代码
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n
}
else {
return n()
}
}
getName('黄四郎'); //'黄四郎'
getName(()=>'张麻子') //'张麻子'
2
3
4
5
6
7
8
9
10
11
12
13
14
# 16.2 type 定义对象类型 与interface 相同
interface User{
name:string,
age:number
}
let user:User = {name:'张麻子',age:30};
2
3
4
5
type User = {
name:string,
age:number
}
let user:User = {name:'张麻子',age:22};
2
3
4
5
# 16.3 interface 可以合并多个类型,type 不支持
interface User{
name:string,
age:number
}
interface User {
like:string,
}
let user:User = {name:'张麻子',age:22,like:'xxxxx'};
2
3
4
5
6
7
8
9
type User = {
name:string,
age:number
}
type User = { //报错 User 标识符重复
like:string,
}
2
3
4
5
6
7
8