Typescript

一、Typescript简介

TypeScript是JavaScript类型的超集,可以简单理解为是JavaScript的“进化版”,它可以编译成纯JavaScript。
TypeScript 和 JavaScript 不同之处在于,它可以在编写代码的时候,就对一些错误进行提示,还能在使用某个数据的时候,为你列出这个数据可以访问的属性和方法。
TypeScript的开发体验远远超过以往纯JavaScript的开发体验,无需运行程序即可修复潜在bug。
TypeScript可以构建大型程序,并在任何浏览器、任何计算机和任何操作系统上运行,且是开源的。

1、特点

  1. 类型检查,语法提示
    在TS中允许为变量指定类型。当你给已定义类型的变量赋值的时候,值的类型不对,便会有错误提示

  2. 约束类型,减少不必要的代码逻辑

  3. 代码重构
    Typescript 支持类型最大的好处是可读性。 类型可以给开发者更多的信息,是最有价值的文档。类型很好的体现了代码即文档这一思想。

2、开发条件

  • 大型项目,代码量较多
  • 开源项目,尤其是工具函数或组件库
  • 进行封装,组件化开发的时候(有一定开发基础)

3、支持TypeScript的编辑器

TypeScript 最大的优势之一便是增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等。 主流的编辑器都支持 TypeScript,推荐使用 Visual Studio Code,除此外还有:

  • Sublime Text
  • Atom
  • WebStorm
  • Vim
  • Emacs
  • Eclipse
  • Visual Studio 2015
  • Visual Studio 2013
  • IDEA 系列

二、基础类型

1、字符串 string

// 普通字符串
let name = ref<string>("jeasu");

// 带变量的字符串
let msg = ref<string>(`my name is ${name.value}`);

// 拼接的字符串
let sentence = ref<string>("Hello, my name is " + name + ".\n\n" +  "I'll be " + (age + 1) + " years old next month.");

2、布尔类型 boolean

// 布尔类型 
let isAdult = ref<boolean>(false); 

3、数字 number

// 数值类型
let age = ref<number>(17);

4、任意值 any

如果是一个普通类型,在赋值过程中改变类型是不被允许的,任意值(Any)用来表示允许赋值为任意类型。

let a1 = ref<string>('seven'); 
a1 = 7;//error 

//但如果是 any 类型,则允许被赋值为任意类型。 
let sim = ref<any>("seven");
sim = 7;
console.log(sim, "任意类型"); //7

//变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型 
let a3; 
a3 = 'seven'; 
a3 = 7; 
//相当于 
let a3:any; 
a3 = 'seven'; 
a3 = 7; 

//any定义多元数组
let arr = reactive<any[]>([1, true, "hellow"]);
arr[1] = 100;
console.log(arr, "any定义多元数组");   // [1,100,'hellow']

5、数组 array

两种方式定义数组:

  1. 元素类型后面加上[ ]
let list1 = ref<number[]>([1, 2, 3]);
let list2 = ref<string[]>(["1", "2", "a"]);
let list3 = ref<any[]>([1,true,'abc',{id:2}])
  1. 使用数组泛型,Array<元素类型>
let hobbies = ref<Array<string>>(["历史", "地理", "生物"]);
let list4 = ref<Array<number | string>>(['dasahk',10])

6、联合类型

一个变量定义可能的多种类型

 // 联合类型
let collection1 = ref<number | string | boolean>(6);
let collection2 = ref<number | string | boolean>("good");
let collection3 = ref<number | string | boolean>(true);

// 联合类型数组
let collection4 = ref<(number | string | boolean)[]>(["hasd", true, 36]);

//联合类型的注意点:对联合类型的数据的操作
// const fn = (str: string | number): number => {
//   return str.length;  //红波浪线报错,原因是number 没有length属性
// };
// fn("hello");

7、元组类型 Tuple

元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同,且数组各项类型与定义一一对应。元组的长度和类型不可变。

let arr1 = ref<[number, string]>([8, "haode"]);

8、Null 和Undefined

默认情况下Null 和Undefined是所有类型的子类型。
可以把 Null 和Undefined赋值给number类型的变量.

// undefind类型
let u: undefined = undefined; 

// null类型
let n: null = null;

9、Never 类型

表示永远不会存在值的类型,常用来定义抛出异常或根本就不会有返回值的函数
never 类型是任何类型的子类型,也可以赋值给任何类型;没有类型是never的子类型或者可以赋值给类型(除了never本身)
never 和 void 之间的区别是void 意味着至少要返回一个 undefined 或者 null ,而 never 意味着不会正常执行到 函数的终点。

// 返回never的函数必须存在无法达到的终点
 const error = (message: string): never => {
    throw new Error(message);
 };

const infiniteLoop = (): never=> {    
    while (true) {     } 
}
// 推断的返回值类型为never
const fail = ()=> {     
    return error("Something failed"); 
}

10、Object 类型

object 表示非原始类型,也就是除number,string,boolean,symbol,null或者undefined之外的类型,使用object类型,就可以更好的表示想Object.create这样的API

 const create = (o: object | null): void => {};

 create({ prop: 0 }); // OK
 create(null); // OK

 // create(42); // Error
 // create("string"); // Error
 // create(false); // Error
 // create(undefined); // Error

11、类型断言

类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。
方式有两种:
一是尖括号语法:

let someValue: any = "this is a string";  
let strLength: number = (<string>someValue).length;

另一个为as语法:

let someValue: any = "this is a string";  
let strLength: number = (someValue as string).length;

12、类型别名 type

 type abc = string | number[];
 type n = number;

 let n1: abc;
 n1 = "4";
 n1 = [1];

 const fn1 = (str: abc): n => {
   return str.length;
 };
 fn1("a");

三、接口 Interfaces

接口可以用于对 “对象的形状(Shape)” 进行描述。
注意:顺序可以乱,但是定义的对象要受到接口的约束。

//定义对象属性
 interface defineObj {
      readonly name: string; //只读属性
      age: number; //必填
      sex?: string; //选填
      call(): string; //有返回值,类型为string
      action(): void; //必填、无返回值
      [propName: string]: any; //任意属性
      //需要注意的是,一旦定义了任意属性,那么确定属性和可选属性都必须是它的子属性
 }
 const obj = reactive<defineObj>({
      name: "abc", //只读,不可修改
      age: 18, //sex:'男',
      a: "a",
      b: 9,
      c: true,
      call: () => "call",
      action: function () {
        console.log("void接口");
      },
      o: { id: 1 },
 });
 console.log(obj.call()); //call
 obj.action(); //void接口
 return {
   	  obj,
 };

接口在函数中的应用:

  1. 当传入的参数是对象类型的时候:
//不用接口定义时
const fn = (obj: { id: number; name: string }): string => {
  return obj.name;
};
const oh = reactive({ id: 3, name: "jeasu" });
console.log(fn(oh)); //jeasu

//仅对函数入参做约束
interface params {
  id: number;
  name: string;
  age: number;
}
const fn1 = (o: params): number => {
  return o.age;
};
const oh1 = reactive({ id: 3, name: "jeasu", age: 18 });
console.log(fn1(oh1)); //18

//对整个函数的类型检查,建议对返回值类型也要定义
iinterface SearchFun {
  (a: string, b: string): boolean;
}
const fn2: SearchFun = (s1, s2) => {
  let i = s1.search(s2);
  return i !== -1;
  //   return s1;
};
console.log(fn2("dsdahjk", "jk"));  // true

  1. 接口继承接口
// 二维坐标接口
interface TwoDPoint{
    x: number,
    y: number
}
//三维坐标中的z坐标接口
interface ThreeDPoint{
    z: number
}

//四维坐标接口继承了二维坐标接口的x,y坐标和三维接口的z坐标
interface FourDPoint extends ThreeDPoint, TwoDPoint{
    //内还定义了四维坐标独有的时间坐标
    time: Date
}
//实例化四维坐标接口
const poi2 = reactive<FourDPoint>({
  z: 100,
  x: 200,
  y: 300,
  time: new Date(),
});
console.log(poi2, "poi2");//Proxy对象{{z: 100,x: 200,y: 300,time: Mon Oct 11 2021 15:29:15 GMT+0800 (中国标准时间)}}

四、泛型 Generics

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
语法格式: <变量类型>
当定义相同结构的函数,但是传入参数类型不同时,若是根据类型依次写容易造成代码冗余,比如:

function fnn(x:number,y:number):number[]{
    return [x,y]
}

function fnn(x:string,y:string):string[]{
    return [x,y]
}

function fnn(x:boolean,y:boolean):boolean[]{
    return [x,y]
}

这时可以将类型用一个变量替代,在使用的时候,将变量类型传过去。

1、泛型变量

泛型变量 T ,T 表示任何类型,也可以用其他字母代替
因此上面的代码可以写成:

//函数声明方式
function declaration<T>(x: T, y: T): T[] {
 	return [x, y];
}

//函数表达式方式
const expression = <T>(n1: T, n2: T): T[] => {
    return [n1, n2];
};
// 单类型
console.log(declaration<string>("1", "2")); //['1','2']
console.log(expression<boolean>(true, false)); //[true,false]
console.log(expression<number>(6, 7)); //[6,7]

//联合类型
 console.log(expression<number | string>(6, "a")); //[6,"a"]
 
//当我们不给传类型的时候,类型推断编译器会自动的帮我们判断传入的是什么类型,此时传入的数据只能为单一类型
console.log(expression(1, 23)); //[1,23]

//泛型的约束
//错误示范 求变量的长度
// let variable1 = <T>(str: T): number => {
//   return str.length;     // T是变量,未指定类型,未必会有length这个属性
// };
//修改:
// 给参数限制类型
let limit1 = <T>(str: string | T[]): number => {
	return str.length;
};
console.log(limit1<number>([1, 3])); //2

//或者给泛型变量添加约束 extends
let limit2 = <T extends String>(arr: T): number => {
   return arr.length;
};
console.log(limit2<string>("one")); //3


//泛型的接口约束:就是让这个变量必须有length属性,此时就限定了变量类型有length属性,只能为string ,或者数组类型
interface ILengthNum {
  length: number;
}
const limit3 = <T extends ILengthNum>(str: T): number => {
  return str.length;
};
console.log(limit3<string>("oneworld")); //8
console.log(limit3<string[]>(["dasjd", "dhksah", "dahskdha"])); //3
console.log(limit3<number[]>([12, 456, 79, 465])); //4

//多个类型参数
const multi = <N, S>(sum: [N, S]): [S, N] => {
  return [sum[1], sum[0]]; //实现的是交换数组内两项的位置
};
console.log(multi<number, string>([1, "one"])); //["one",1]

2、泛型接口

//泛型接口
interface genface1 {
   <T>(a: T, b: T): boolean;
}
const func1: genface1 = (x, y) => {
   return x == y;
};
console.log(func1<number>(1111, 5)); //false
console.log(func1<string>("abc", "abc")); //true

//另一种 把泛型参数提前放在接口名上
interface genface2<T> {
  (a: T, b: T): boolean;
}
const func2: genface2<number> = (x, y) => {
  return x == y;
};
console.log(func2(7, 5)); //false

//另一种写法,先定义类型,再赋值函数
let func3: genface2<string>;
func3 = (x, y) => {
  return x == y;
};
console.log(func3("abc", "abc")); //true

//多类型泛型接口
interface createA3<N, T> {
  (a: N, b: T): Array<T>;
}
let func4: createA3<number, string>;
func4 = function (i, s) {
  let arr: string[] = [];
  arr[i] = s;
  return arr;
};
func4(1, "dqwy");

//泛型约束
interface Length4 {
  length: number;
}
interface createA4<N, T extends Length4> {
  (a: N, b: T): string;
}
let func5: createA4<number, string>;
func5 = function (i, s) {
  //var arr:string[] = []
  //return arr
  return s;
};
func5(2, "dqwy");

3、用泛型变量和any的区别

//使用泛型变量 T
function fun6<T>(x: T, y: T): T[] {
  return [x, y];
}
fun6<string>("a", "b");

//使用 any:缺点就是传入的类型和返回的类型不确定
function fun7(x: any, y: any): any[] {
  return [x, y];
}
fun7("a", "b");

五、类 class

类是属性和函数的集合,是生成对象(Object)或类实例的模板。

1、类的定义和使用


//类的定义和使用
class MyInfo { //class是关键字,类名默认全部大写首字母
  name: string; //属性
  weather: string; //属性
  
  constructor(name: string, weather: string){ //构造函数,一般用于初始化。如果没有,TS会自动生成一个,以备用new创建类实例时调用。
    this.name = name;
    this.weather = weather;
  }
  printInfo(): void { //其它函数,无返回值
    console.log(`Hello, ${this.name}.`);
    console.log(`Today is ${this.weather}.`);
  }
}

let myData = new MyInfo('QiGe', 'raining'); //使用new关键字生成对象,即该类的实例
myData.printInfo();
          

2、类的属性和函数的访问权限

类中的属性和函数都有访问权限,默认为public即全局可访问,其次为protected即可在类的内部和其子类的内部可访问,最后为private,只能在该类内部可访问。


//访问权限
class MyInfo { //class是关键字,类名默认全部大写首字母
  public name: string; //public属性,可省略
  private _weather: string; //私有属性,习惯以_开头进行命名
  
  constructor(name: string, weather: string){ //构造函数,一般用于初始化
    this.name = name;
    this._weather = weather;
  }
  printInfo(): void { //其它函数
    this._test();
    console.log(`Hello, ${this.name}.`);
    console.log(`Today is ${this._weather}.`);
  }
  private _test(): void {
    console.log('You can not call me outside!');
  }
}

let myData = new MyInfo('QiGe', 'raining'); //使用new关键字生成对象
console.log(myData._weather); //error!
myData._test(); //error
myData.printInfo();
          

3、存取器getter、setter

当在类外部时,建议设置getter和setter操作其private属性,即使public属性也如此。


//getter和setter
class MyInfo { //class是关键字,类名默认全部大写首字母
  private readonly _name: string; //私有属性,外部不可访问。readonly使其只能在初始化时赋值,以后不可更改。    
  private _weather: string; //私有属性,习惯以_开头进行命名

  constructor(name: string, weather: string){ //构造函数,一般用于初始化
    this._name = name;
    this._weather = weather;
  }
  get name(): string {
    return this._name;
  }
  set name(value: string) {  //error! _name有readonly属性
    this._name = value;
  }
  get weather(): string {
    return this._weather;
  }
  set weather(value: string) {
    this._weather = value;
  } 
}
  
let myData = new MyInfo('QiGe', 'raining'); //使用new关键字生成对象
console.log(myData.name, myData.weather);
myData.weather = 'sunny'; //OK
myData.name = 'Wang'; //error!
console.log(myData);
          

4、静态属性

类中的属性或函数有static修饰,则可直接使用而不需要实例化


//静态属性,内建或自定义,无需new即可使用
console.log(Math.round(89.64)); //90
console.log(Math.pow(2, 8)); //256
class MyStaticClass {
  static place = 'Earth';
  static printInfo() {
    console.log('We have only one Earth!');
  }
}
console.log(MyStaticClass.place);
MyStaticClass.printInfo();
          

5、继承

可以通过extends关键字继承其它类,从而成为其子类


class Animal {
  // 当构造函数传入的参数加上了“访问权限控制符”,则同时会声明同名类属性,并赋值
  constructor(public name: string) { }
  protected log(message: string) {
    console.log(message);
  }
  move(distanceInMeters: number = 0) {        
    this.log(`${this.name} moved ${distanceInMeters}m.`);//请注意name来自何处
    this.log('==============');
  }
}

class Horse extends Animal {
  constructor(name: string) { 
    super(name); // 通过super调用父类构造器
  }
  run(distanceInMeters = 50) { //自己独有的函数
    this.log("Clop, clop..."); 
    super.move(distanceInMeters); // 通过super调用父类方法
  }
}

class Eagle extends Animal {
  constructor(name: string) { super(name); }
  reborn() { //自己独有的函数
    console.log('Reborn? It is a joke, hahaha!');
  }

}

let tom: Horse = new Horse("Tommy the Palomino");
tom.run(8964);
let sam: Eagle = new Eagle("Sammy the Hawk");
sam.move(1024);//sam的move函数来自何处?
sam.reborn();
          

6、类实现接口 (implements)

interface ClassType {
 name: string;
  action(): void;
}
class Obj implements ClassType {
  //类Obj受到接口ClassType的约束
  // 类中的属性和方法要受到接口的约束,但是添加属性或方法不会报错
  //属性和方法只能比接口里定义的多,不能少
  name: string;
  age: number; //新增属性,接口里未定义
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  action() {
    console.log("执行");
  }
  a() {
    return "sah";
  } //新增方法,接口未定义
}
const obj1 = new Obj("hahaha", 18);
console.log(obj1.name); //hahaha
console.log(obj1.age); //18
console.log(obj1.a()); //sah
obj1.action(); //执行

7、泛型在类中的运用

class A2<T> {
  n: T;
  constructor(num: T) {
    this.n = num;
  }
  add(x: T): T {
    return x;
  }
}
var a2 = new A2<number>(1);
console.log(a2.n); //1
console.log(a2.add(3)); //3

六、模块Module

对于大型的项目,需要使用模块进行管理。每个 .ts 文件就是一个模块,通过 export 来对外部模块暴露元素,通过 import 来引入模块。

小结

关于Typescript的基础学习大概就到这里啦,前端的学习路还远呢,加油吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值