TypeScript基础学习
TypeScript简介
- TypeScript是JavaScript的超集。
- 它对JS进行了扩展,向JS中引入了类型的概念,并添加了许多新的特性。
- TS代码需要通过编译器编译为JS,然后再交由JS解析器执行。
- TS完全兼容JS,换言之,任何的JS代码都可以直接当成JS使用。
- 相较于JS而言,TS拥有了静态类型,更加严格的语法,更强大的功能;TS可以在代码执行前就完成代码的检查,减小了运行时异常的出现的几率;TS代码可以编译为任意版本的JS代码,可有效解决不同JS运行环境的兼容问题;同样的功能,TS的代码量要大于JS,但由于TS的代码结构更加清晰,变量类型更加明确,在后期代码的维护中TS却远远胜于JS。
TypeScript 开发环境搭建
- 下载Node.js
64位:https://nodejs.org/dist/v14.15.1/node-v14.15.1-x64.msi
32位:https://nodejs.org/dist/v14.15.1/node-v14.15.1-x86.msi - 安装Node.js
- 使用npm全局安装typescript
进入命令行
输入:npm i -g typescript - 创建一个ts文件
- 使用tsc对ts文件进行编译
进入命令行
进入ts文件所在目录
执行命令:tsc xxx.ts(执行后就可以将ts文件编译成js文件)
TS类型声明
- 类型声明是TS非常重要的一个特点
- 通过类型声明可以指定TS中变量(参数、形参)的类型
- 指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错
- 简而言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值
自动类型判断 - TS拥有自动的类型判断机制
- 当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型
- 所以如果你的变量的声明和赋值时同时进行的,可以省略掉类型声明
TS类型
类型展示
类型 | 例子 | 描述 |
---|---|---|
number | 1, -33, 2.5 | 任意数字 |
string | ‘hi’, “hi”,‘hi’ | 任意字符串 |
boolean | true、false | 布尔值true或false |
字面量 | 其本身 | 限制变量的值就是该字面量的值 |
any | * | 任意类型 |
unknown | * | 未知类型的值(类型安全的any) |
void | 空值(undefined) | 没有值(或undefined)(以函数为例就是没有返回值) |
never | 没有值 | 不能是任何值 (永远不会返回结果) |
object | {name:‘孙悟空’} | 任意的JS对象 (可以指定有哪些属性以及属性的类型) |
array | [1,2,3] | 任意JS数组 |
tuple | [4,5] | 元素,TS新增类型,固定长度数组 |
enum | enum{A, B} | 枚举,TS中新增类型 |
字面量类型
any
unknown
unknown表示未知类型的值,和any功能相似,但是比any安全
解决以上问题的办法:
never
object
可选值要放在最后一个位置上
枚举
类型断言
- 有些情况下,变量的类型对于我们来说是很明确,但是TS编译器却并不清楚,此时,可以通过类型断言来告诉编译器变量的类型,断言有两种形式:
第一种:采用as
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;
第二种:在前面加上指定的类型
let someValue: unknown = "this is a string";
let strLength: number = (<string>someValue).length;
面向对象
面向对象的理解
一切操作都要通过对象,也就是所谓的面向对象,那么对象到底是什么呢?这就要先说到程序是什么,计算机程序的本质就是对现实事物的抽象,抽象的反义词是具体,比如:照片是对一个具体的人的抽象,汽车模型是对具体汽车的抽象等等。程序也是对事物的抽象,在程序中我们可以表示一个人、一条狗、一把枪、一颗子弹等等所有的事物。一个事物到了程序中就变成了一个对象。
在程序中所有的对象都被分成了两个部分数据和功能,以人为例,人的姓名、性别、年龄、身高、体重等属于数据,人可以说话、走路、吃饭、睡觉这些属于人的功能。数据在对象中被成为属性,而功能就被称为方法。所以简而言之,在程序中一切皆是对象。
class 类
要想面向对象,操作对象,首先便要拥有对象,那么下一个问题就是如何创建对象。要创建对象,必须要先定义类,所谓的类可以理解为对象的模型,程序中可以根据类创建指定类型的对象,举例来说:可以通过Person类来创建人的对象,通过Dog类创建狗的对象,通过Car类来创建汽车的对象,不同的类可以用来创建不同的对象。
类简介.ts
class Person{
/*
* 直接定义的属性是实例属性,需要通过对象的实例来访问
* const per = new Person() per.age
*
* 使用static开头的属性是静态属性(类属性),可以直接通过类来访问
* Person.age
*
* readonly开头的属性表示一个只读的属性,无法修改
*/
name:string = '张三'
age:number = 25
// 只读
static readonly sex:string = '男'
sayHello(){
console.log('你好')
}
// 静态方法(类方法)
static sayHi(){
console.log('static Hi')
}
}
const per = new Person()
console.log(per)
// 访问实例属性和实例方法
console.log(per.name,per.age)
console.log(per.sayHello)
// 访问类属性和类方法
console.log(Person.sex)
console.log(Person.sayHi)
构造函数
class Dog{
name :string;
age:number;
// constructor被称为构造函数,构造函数会在对象创建(实例化)时调用
constructor(name:string,age:number){
// 在实例方法中,this就表示当前的实例
// 在构造函数中当前对象就是当前创建的那个对象
// 可以通过this向新创建的对象中添加属性
this.name = name
this.age = age
}
bark(){
console.log(this)
}
}
// 创建多个实例对象,并且赋不同的值
// 构造函数带有形参,创建实例对象的时候就必须传参
const dog = new Dog('旺财',2)
dog.name ='橘子' //修改dog实例的name属性
const dog1 = new Dog('旺福',5)
dog.bark();
dog1.bark();
继承和重写
- 继承时面向对象中的又一个特性
- 通过继承可以将其他类中的属性和方法引入到当前类中
- 通过继承可以在不修改类的情况下完成对类的扩展
- 发生继承时,如果子类中的方法会替换掉父类中的同名方法,这就称为方法的重写
class Animal{
name:string;
age:number;
constructor(name:string,age:number){
this.name = name
this.age = age
}
sayHello(){
console.log('动物在叫~~~~~')
}
}
/*
*Dog extends Animal
* -此时,Animal类被称为父类,Dog类被称为子类
* -使用继承后,子类将会拥有父类的所有方法和属性
* -通过继承可以将多个子类中共有的代码写在一个父类中
* --如果希望子类拥有一些父类中没有的属性或方法,就直接添加在子类中
* ——如果在子类中添加了和父类相同的方法,则子类方法会覆盖掉父类的方法,这就叫方法重写
*/
class Dog extends Animal{
sayHello(){
console.log('狗在叫')
}
dogSay(){
console.log('汪汪汪')
}
}
class Cat extends Animal{
sayHello(){
console.log('猫在叫')
}
catSay(){
console.log('喵喵喵')
}
}
const dog = new Dog('发财',2)
const cat = new Cat('咪咪',1)
dog.sayHello()
cat.sayHello()
console.log(dog,cat)
super
class Animal{
name:string;
constructor(name:string){
this.name = name
}
sayHello(){
console.log('动物在叫~~~~~')
}
}
class Dog extends Animal{
age:number;
/*
* 如果在子类中书写了构造函数,在子类构造函数中必须对父类的构造函数进行调用,利用super
*(原因:子类中重写构造函数后,就会将父类中的构造函数覆盖掉,这样父类中的name属性就没有被初始化,就会报错)
*/
constructor(name:string,age:number){
super(name);
this.age = age
}
sayHello(){
super.sayHello();
}
}
const dog = new Dog('发财',2)
dog.sayHello()
抽象类
/*
* 以abstract开头的类是抽象类
* 抽象类和其他类区别不大,只是不能用来创建对象
* 抽象类就是专门用来呗继承的类
*
* 抽象类中可以添加抽象方法
*/
abstract class Animal{
name:string;
constructor(name:string){
this.name = name
}
//定义一个抽象方法,抽象方法使用abstract开头,没有方法体
// 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
abstract sayHello():void;
}
class Dog extends Animal{
age:number;
constructor(name:string,age:number){
super(name);
this.age = age
}
sayHello(){
console.log('汪汪汪')
}
}
const dog = new Dog('发财',2)
dog.sayHello()
接口
接口的作用类似于抽象类,不同点在于接口中的所有方法和属性都是没有实值的,换句话说接口中的所有方法都是抽象方法。接口主要负责定义一个类的结构,接口可以去限制一个对象的接口,对象只有包含接口中定义的所有属性和方法时才能匹配接口。同时,可以让一个类去实现接口,实现接口时类中要保护接口中的所有属性。
/*
* 接口可以在定义类的时候去限制类的结构,
* 接口中的所有属性都不能有实际的值
* 接口只定义对象的结构,而不考虑实际值
* 在接口中的所有方法都是抽象方法
*/
interface Myinterface{
name:string;
sayHello():void;
}
// 定义类时,可以使类去实现一个接口,实现接口就是使类满足接口的需求
class Animal implements Myinterface{
name: string;
constructor(name:string){
this.name = name
}
sayHello(){
console.log('大家好!!!!')
}
}
属性的封装
class Dog {
/*
* public修饰的属性可以在任意位置访问和修改(默认为public) 数据不安全
* private 私有属性,私有属性只能在类内部进行访问,子类中也不可以访问
* ——通过在类中添加getter setter方法使得私有属性可以被外部访问
* protected 受保护的属性,可以在当前类以及当前类的子类中访问
*/
private _name: string;
private _age: number;
constructor(name: string,age: number){
this._name = name
this._age = age
}
// TS中设置getter setter的方式,这种方式设置完后访问的方式不变 dog.name dog.name='大宝'(不用通过调用方法来访问和修改)
// 注意变量名的书写方式
get name(){
return this._name
}
set name(value){
this._name = value
}
get age(){
return this._age
}
set age(value){
if(value>=0){
this._age = value
}
}
}
let dog = new Dog('旺福',2)
console.log(dog.name)
dog.name='大宝'
console.log(dog.name)
class C{
// 可以直接将属性定义在构造函数中
// 这种方式写构造函数,实例化对象传参后属性的复制就自动完成了
constructor(public name:string,public age:number){
}
}
let c = new C('ls',25)
console.log(c)
泛型
定义一个函数或类时,有些情况下无法确定其中要使用的具体类型(返回值、参数、属性的类型不能确定),此时泛型便能够发挥作用。
/*
* 在定义函数或者是类的时候,如果遇到类型不明确就可以使用泛型
*/
function fn<T>(sex:T):T{
return sex
}
// 直接调用具有泛型的函数
let result = fn('男') //不指定泛型,TS可以自动对类型进行判断,sex为字符串,函数返回类型也为字符串类型
let result1 = fn<number>(1) //指定泛型,sex为number类型,函数返回类型也为number类型
// 泛型可以同时指定多个
function fn1<T,S>(a:T,b:S):T{
console.log(b)
return a
}
let result2 = fn1(1,2)
let result3 = fn1<string,number>('hello',2)
// 泛型也可以实现接口
interface Inter{
length:number
}
// T extends Inter 表示泛型T必须是Inter实现类(子类)
function fn2<T extends Inter>(a:T){
return a.length
}
let result4 = fn2('zhangsan')
let result5 = fn2({length:5,name:'ls'})
// 类 使用泛型
class MyClass<T>{
name:T;
constructor(name:T){
this.name = name
}
}
let my = new MyClass(10)
let my1 = new MyClass('kj')