TypeScript学习笔记

一、TypeScript定义

TypeScript是JavaScript的超集,扩展了JavaScript的语法,TypeScript通过类型注解提供编译时的静态类型检查。

二、TypeScript基础类型

2.1 Boolean类型

//声明完变量直接进行赋值
let c: boolean = false;
复制代码

如果变量的声明和赋值同时进行的,ts可以自动对变量进行类型检查

let c= false;
c = true;
// c = 123; 此行代码会报错,因为变量a的类型时boolean,不是赋值数字
复制代码

2.2 Number类型

let a:number;
//a 的类型设置为number,在以前的使用过程过程中a的值只能是数字

a = 10;
// a = 'hello';//此行代码会报错,因为变量a的类型时number,不是赋值字符串
复制代码

2.3 String类型

let b: string;
b = 'hello';
复制代码

2.4 Array类型

let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3]; //Array<number> 泛型语法
复制代码

String[] 表示字符串数组

let e1: string[];
e1 = ['a','b','c'];
复制代码

2.5 元组

tuple(元组):就是固定长度的数组
语法:[类型, 类型, 类型]

let h: [string, number];
h = ['hello', 123];
复制代码

2.6 枚举

enum Gender{
    Male = 0,
    Female = 1,
}
let i: {name: string, gender: Gender};
i = {
    name: '孙悟空',
    gender: Gender.Male//'male'
}

console.log(i.gender === Gender.Male)
复制代码

2.7 any类型

any 表示的是任意类型,一个变量设置类型为any后相当于对该变量关闭了TS的类型检测
使用TS时,不建议使用any类型

let d:any;
复制代码

声明变量如果不指定类型,则TS解析器会自动判断变量的类型为any(隐式的any)

let d;
d = 10;
d = 'hello';
d = 'true';
复制代码

d的类型时any,它是可以赋值给任意变量

let d:any;
let s:string;
s = d;
复制代码

2.8 Unknown 类型

let e: unknown;
e = 10;
e = true;
e = "hello";
复制代码
  • unknown 实际上就是一个类型安全的any
  • unknown 类型的变量,不能直接赋值给其他变量
let s:string;
// s = e; // 报错: 不能将类型“unknown”分配给类型“string”
if (typeof e === "string") {
    s = e;    
}
复制代码

2.9 Void类型

void 用来表示空,以函数为例,就表示没有返回值的函数

function fn2(): void{
}
复制代码

2.10 Never 类型

never: 表示永远不会返回结果;没有值

function fn3(): never{
    throw new Error("报错了!");
}
复制代码

2.11 Null 和 Undefined 类型

TypeScript 里,undefined 和 null 两者有各自的类型分别为 undefined 和 null

let u: undefined = undefined;
let n: null = null;
复制代码

2.12 object类型

object 表示一个js对象

let a:object;
a = {};
a = function(){

};
复制代码
  • { } 用来指定对象中可以包含哪些属性
  • 语法:{属性名:属性值,属性名:属性值}
  • 在属性名后面加上?,表示属性是可选的
let b: {name: string, age?:number};
复制代码

age后面没有?的话,下面第一个就会报错

b = {name: 'stonet'};
b = {name: "孙悟空", age: 18};
复制代码

下面赋值会报错;对象文字可以只指定已知属性,并且“c”不在类型“

let c3: {name: string, a?:number, b?:number};
c3 = {name:"猪八戒", a:1, b:2,c:3}
复制代码

[propName: string]:any 表示任意类型的属性

let c: {name: string, [propName: string]:any}
c = {name:"猪八戒", age: 18, gender: '男'}
复制代码

设置函数结构的类型声明:
语法: (形参:类型,形参:类型…)=> 返回值

let d1: (a:number ,b: number)=>number;
d1 = function (n1: number, n2: number): number {
    return n1 + n2
}
复制代码

2.13 类型的别名

type myType = 1 | 2 | 3 | 4 | 5;
let k: myType;
let l: myType;

let m: myType;

k = 2;
m = 6; // 不能将类型“6”分配给类型“myType”
复制代码

2.14 类型断言

类型断言: 可以用来告诉解析器变量的实际类型
语法:

  1. 变量 as 类型
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
复制代码
  1. <类型>变量
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
复制代码

2.15 联合类型

可以使用 | 来连接多个类型(联合类型)

let b1: "male" | "female";

b1= "male";
b1= "female";

let c1 : boolean | string;

c1 = true;
c1 = 'hello';
复制代码

三、TypeScript 类

3.1 类的简介

使用class关键字来定义一个类,对象中主要包含两个部分:
属性
方法

class Person{

    /* 
        直接定义的属性是实例属性,需要通过对象的实例去访问;
            const per = new Person();
            per.name
        使用static的开头的属性是静态属性(类型),可以直接通过类去访问
            Person.age
        readonly 开头的属性表示一个只读的属性无法修改
    */
    //定义实例属性
    // name: string = '孙悟空';

    // readonly name: string = '孙悟空';
    name = '孙悟空';
    // 在属性前使用static关键字可以定义类属性(静态属性)
    // static readonly age: number = 18;
    age = 18;

    //定义方法
    /* 
        如果方法以static开头则方法就是类方法,可以直接通过类去调用
    */
    sayHello(){
        console.log("Hello 大家好!")
    }
}

//new 一个实例
const per = new Person();

console.log(per.name);

per.name = 'tom';
console.log(per.name) 

// console.log(Person.age); //static时可以使用

// Person.sayHello();

per.sayHello();
复制代码

3.2 构造函数

class  Dog {
    name: string;
    age: number;

    // constructor 被称为构造函数
    //  构造函数会在对象创建时调用
    constructor(name: string, age: number) {
        // 在实例方法中,this就表示当前的实例
        // 在构造函数中当前对象就是当前新建的那个对象
        // 可以通过this向新建的对象中添加属性
        // console.log(this);
        this.name = name,
        this.age = age
    }

    bark(){
        alert('汪汪汪!!');
        console.log(this.name);
    }
}

const dog = new Dog('小黑', 4);
const dog2 = new Dog('小白', 2);
// const dog3 = new Dog(); // 应有 2 个参数,但获得 0 个
// const dog4 = new Dog();

console.log(dog);
console.log(dog2);
// console.log(dog3);
// console.log(dog4);

dog.bark();

复制代码

3.3 继承

(function (){
    // 定义一个Animal类
    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被称为子类
            - 使用继承后,子类将会拥有父类所有的方法和属性
            - 通过继承可以将多个类中共有的代码写在一个父类,
                这样只需要写一次即可让所有的子类都同时拥有父类中的属性和方法
            - 如果子类中添加了和父类相同的方法,则子类方法会覆盖掉父类的方法
                这种子类覆盖掉父类方法的形式,我们称为重写
    */
    //定义一个表示狗的类
    //使Dog类继承Animal类
    class Dog extends Animal{
        // name: string;
        // age: number;

        // constructor(name: string, age: number){
        //     this.name = name;
        //     this.age = age;
        // }

        // sayHello(){
        //     console.log('汪汪汪!!');
        // }
        run(){
            console.log(`${this.name}在跑啊~~`);
        }

        sayHello(){
            console.log('汪汪汪!!');
        }
    }

    // 定义一个猫的类
    //使Cat类继承Animal类
    class Cat extends Animal{
        // name: string;
        // age: number;

        // constructor(name: string, age: number){
        //     this.name = name;
        //     this.age = age;
        // }

        // sayHello(){
        //     console.log('喵喵喵!!');
        // }
        sayHello(){
            console.log('喵喵喵!!');
        }
    }


    const dog = new Dog('旺财', 5);
    const cat = new Cat('咪咪', 3);
    console.log(dog);
    dog.sayHello();
    dog.run();
    console.log(cat);
    cat.sayHello();


})();

/* OCP原则
    OPEN CLOSE开闭原则
    对扩展开放,对修改关闭
*/
复制代码

3.4 super关键字

(function () {
    class Animal{
        name: string;
        constructor(name: string){

            this.name = name;
        }
        sayHello(){
            console.log('动物在叫~~')
        }
    }

    class Dog extends Animal{
        age: number
        constructor(name: string, age: number){
            //如果在子类中写了构造函数,在子类构造函数中必须对父类的构造函数进行调用
            super(name);//调用父类的构造函数
            this.age = age;
        }

        sayHello() {
            // 在类的方法中 super就表示当前类的父类
            super.sayHello(); // 调用父类的方法
            console.log('汪汪汪~~')
        }
    }

    const dog = new Dog("旺财", 3);
    dog.sayHello();
})()

//动物在叫~~
//汪汪汪~~
复制代码

3.5 抽象类

(function () {
    
    /* 
        以abstract 开头的类是抽象类
            抽象类和其他类区别不大,只是不能用来创建对象
            抽象类就是专门用来被继承的类
            抽象类中可以添加抽象方法  
    */
    
    abstract class Animal{
        name: string;
        constructor(name: string){
            this.name = name;
        }

        // 定义一个抽象方法
        // 抽象方法使用abstract开头,没有方法体
        // 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
        abstract sayHello():void;
        // sayHello(){
        //     console.log('动物在叫~~')
        // }
    }

    class Dog extends Animal{
        sayHello() {
            console.log('汪汪汪~~')
        }
    }
    
    class Cat extends Animal{
        sayHello() {
            console.log('喵喵喵~~')
        }
    }

    const dog = new Dog("旺财");
    dog.sayHello();

    const an = new Animal();//无法创建抽象类的实例
    dog.sayHello();
})()
复制代码

3.6 接口

(function () {

    // 描述一个对象的类型
    type myType = {
        name: string,
        age: number
    };

    /* 
        接口用来定义一个类结构, 用来定义一个类中应该包含哪些属性和方法
            同时接口也可以当成类型声明去使用    
    */
    interface myInterface{
        name: string;
        age: number;
    }

    interface myInterface{
        gender: string;
    }
    // 接口定义会合并
    const obj: myInterface = {
        name: 'sss',
        age: 111,
        gender: '男'
    };

    /*
        接口可以在定义类的时候去限制类的结构
        接口中所有的属性都不能有实际的值
        接口只定义对象的结构,而不考虑实际值
            在接口中所有的方法都是抽象类   
    */
    interface myInter{
        name: string;
        sayHello(): void;
    }

    /* 
        定义类时,可以使类去实现一个接口
            实现接口就是使类满足接口的要求
    */
    class MyClass implements myInter{
        name: string;

        constructor(name: string){
            this.name = name;
        }

        sayHello(){
            console.log("大家好~~~");
        }        
    }


})();
复制代码

3.7 接口与类型别名的区别

3.7.1 Objects/Functions

接口和类型别名都可以用来描述对象的形状或函数签名

接口

interface Point {
  x: number;
  y: number;
}

interface SetPoint {
  (x: number, y: number): void;
}

复制代码

类型别名

type Point = {
  x: number;
  y: number;
};

type SetPoint = (x: number, y: number) => void;

复制代码

3.7.2 其他类型

与接口类型不一样,类型别名可以用于一些其他类型,比如原始类型、联合类型和元组

// primitive
type Name = string;

// object
type PartialPointX = { x: number; };
type PartialPointY = { y: number; };

// union
type PartialPoint = PartialPointX | PartialPointY;

// tuple
type Data = [number, string];

复制代码

3.7.3 Extend

接口和类型别名都能够被扩展,但语法有所不同。此外,接口和类型别名不是互斥的。接口可以扩展类型别名,而反过来是不行的。

Interface extends interface

interface PartialPointX { x: number; }
interface Point extends PartialPointX { 
  y: number; 
}

复制代码

Type alias extends type alias

type PartialPointX = { x: number; };
type Point = PartialPointX & { y: number; };

复制代码

Interface extends type alias

type PartialPointX = { x: number; };
interface Point extends PartialPointX { y: number; }

复制代码

Type alias extends interface

interface PartialPointX { x: number; }
type Point = PartialPointX & { y: number; };

复制代码

3.7.4 Implements

类可以以相同的方式实现接口或类型别名,但类不能实现使用类型别名定义的联合类型:

interface Point {
  x: number;
  y: number;
}

class SomePoint implements Point {
  x = 1;
  y = 2;
}

type Point2 = {
  x: number;
  y: number;
};

class SomePoint2 implements Point2 {
  x = 1;
  y = 2;
}

type PartialPoint = { x: number; } | { y: number; };

// A class can only implement an object type or 
// intersection of object types with statically known members.
class SomePartialPoint implements PartialPoint { // Error
  x = 1;
  y = 2;
}

复制代码

3.7.5 Declaration merging(声明合并)

interface myInterface{
    name: string;
    age: number;
}

interface myInterface{
    gender: string;
}

const obj: myInterface = {
    name: 'sss',
    age: 111,
    gender: '男'
};
复制代码

3.8 属性的封装

(function(){

    // 定义一个表示人的类
    class Person{
        //TS可以在属性前添加属性的修饰符
        /* 
            public 修饰的属性可以在任意位置访问(修改) 默认值
            private 私有属性, 私有属性只能在类内部进行访问(修改)
                - 通过再类中添加方法使得私有属性可以被外部访问
            protected 受保护的属性,只能在当前类和当前类的子类中访问(修改)
        
        */
        public _name: string;
        private _age: number;

        constructor(name: string, age: number) {
            this._name = name;
            this._age = age;
        }

        /* 
            getter 方法来读取属性
            setter 方法来设置属性
                - 他们被称为属性的存取器
        */

       /*  // 定义方法 用来获取name属性
        getName(){
            return this._name;
        }
        //  定义方法,用来设置name属性
        setName(value: string){
            this._name = value;
        }

        //
        getAge(){
            return this._age;
        }

        setAge(value: number){
            // 判断年龄是否合法
            if(value >= 0){
                this._age = value;
            }
        
        } */
        // TS中设置getter方法的方式
        get name(){
            console.log('get name()执行了!!');
            return this._name;
        }

        set name(value:string){
            this._name = value;
        }

        set age(value:number){
            if(value >= 0){
                this._age = value
            }

        }
    }
    const per = new Person('孙悟空', 18);

    /* 
        现在属性是在对象中设置的,属性可以任意的被修改
            属性可任意被修改将会导致对象中的数据变得非常不安全
    */
    // per._name = '猪八戒';
    // per._age = -38;
    // per.setName('猪八戒');
    // per.setAge(-33);
    // console.log(per);
    per.name = '猪八戒';
    per.age = -13
    console.log(per);


    class A{
        protected num: number;

        constructor(num: number){
            this.num = num;
        }
    }

    class B extends A{
        test(){
            console.log(this.num);
        }
    }

    const b = new B(125);
    // b.num = 33 //属性“num”受保护,只能在类“A”及其子类中访问

    // class C{
    //     name: string;
    //     age: number;
    //     // 可以直接将属性定义在构造函数中
    //     constructor(name: string, age:number){
    //         this.name = name;
    //         this,age = age;
    //     }
    // }

    class C{
        // 语法糖
        // 可以直接将属性定义在构造函数中
        constructor(public name: string, public age:number){

        }
    }

    const c = new C('李明康',18);
    console.log(c);
    
})();
复制代码

四、泛型

在定义函数或是类时,如果遇到类型不明确就可以使用泛型

function fn<T>(a:T):T {
    return a;
}
复制代码

可以直接调用具有泛型的函数

let result = fn(10);// 不指定泛型,TS可以自动对类型进行推断
let result2 = fn<string>('hello');//手动指定泛型
复制代码

泛型可以同时指定多个

function fn2<T, K>(a:T, b:K):T {
    console.log(b);
    return a;
    
}
fn2<number, string>(123, 'hello');
复制代码

T extends Inter 表示泛型T必须是Inter实现类(子类)

interface Inter{
    length: number;
}
function fn3<T extends Inter>(a:T): number {
    return a.length
}
fn3('124');
// fn3(1243); //类型“1243”的参数不能赋给类型“Inter”的参数
fn3({length: 10});
复制代码
class MyClass<T>{
    name: T;
    constructor(name:T){
        this.name = name;
    }
}
const mc = new MyClass<string>('孙悟空');
复制代码

五、tsconfig.json文件

{
/* 
    tsconfig.json 是ts编译器的配置文件,ts编译器可以更具它的信息来对代码进行编译
    "include" 用来表示指定哪些ts文件需要被编译
    路径;
        *表示任意文件,
        **表示任意目录
    "exclude" 用来表示不需要被编译的文件目录
            默认值:{"node_modules", "bower_components", "jspm_packages"}
*/
    "include": [
        "./src/**/*"
    ],
    
    // "exclude": [
    //     "./src/hello/**/*"
    // ]

    //compilerOptions 编译器的选项 
    "compilerOptions": {
        //target 用来指定ts被编译为ES的版本
        //'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext'.
        "target": "ES2015",

        //module 指定要使用的模块化的规范
        //'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'esnext'
        "module": "es2015",
        
        //lib 用来指定项目所用的库
        // 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 
        // 'dom.iterable', 'webworker', 'webworker.importscripts', 'webworker.iterable', 'scripthost', 'es2015.core', 'es2015.collection', 
        // 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include',
        //  'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asyncgenerator', 'es2018.asynciterable', 'es2018.intl', 
        // 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.object', 'es2019.string', 'es2019.symbol', 'es2020.bigint', 'es2020.promise', 
        // 'es2020.sharedmemory', 'es2020.string', 'es2020.symbol.wellknown', 'es2020.intl', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl',


        //outDir 用来指定编译后文件所在的目录
        "outDir": "./dist",

        //outFile 将代码合并为一个文件
        // "outFile": "./dist/app.js",

        // 是否对js文件进行编译,默认是false
        "allowJs": true,

        // 是否检查js代码是否符合语法规范,默认值是false
        "checkJs": true,

        // 是否移除注释
        "removeComments": true,

        // 不生成编译后的文件
        "noEmit": false,

        // 当有错误时不生成编译文件
        "noEmitOnError": true,

        // 所有严格检查的总开关
        "strict": true,

        // 用来设置编译后的文件是否使用严格模式,默认是false
        "alwaysStrict": true,

        // 不允许隐式的any类型
        "noImplicitAny": true,

        // 不允许不明确类型的this
        "noImplicitThis": true,

        // 严格检查空值
        "strictNullChecks": true


    }
}

复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享