TypeScript 基础知识笔记

19 阅读7分钟

1 定义变量并声明类型

1.1 原始数据类型

let username: string = '小明'; // 字符串
let count: number = 100; // 数字,支持 十/十六/二/八 进制
let flag: boolean = true; // 布尔,只能是 true/false
let u: undefined = undefined; // undefined,用处不是很大
let timer: null = null; // null,用处不是很大
function gerName(str: string): void{} // 空 void,没有任何类型,函数的没用返回值的使用 void ,返回值为空( undefined )
let obj: any // 任意类型
// 这里当类型不确定的时候,就可以使用 any 任意类型,不到万不得已不使用
// Unknow 类型和 any 一样可以容纳任意类型比 any 安全

// 字面量
let animal: 'dog' // 字面量
animal = 'cat' // 报错
animal = 'dog' // 通过

1.2 复杂类型

let arr: number[] = [1,2,3,4,5,true] // 数组 array, 报错,true 不属于 number
let let tuple1: [string, boolean] = ['测试', true]; // 元祖 tuple
// 接口 interface
interface Device {
    readonly id: number // 不可改变,定义完后不能更改
    type: string
    width: number
    height: number
    color?: string // 添加了 ? 代表可以没有
}
// 定义  phone 的类型为 Device ,Device 上定义的 id 只能赋值一次,属性 type、width、height 必须有,属性 color 可以没有
let phone: Device = {
    id: 1,
    type: '手机',
    width: 0.07',
    height: 0.18',
};


//   输入类型number          number    返回类型number
function sum(num1: number, num2: number, num3?: number): number{
    return num1 + num2
}
const sum1 = (num1: number, num2: number): number => {
    return num1 + num2;
}

// () 里面是输入的形参的类型 
// => 代表是 返回值 的类型
// : 后面的都是声明类型,和代码逻辑没有什么关系
let mysum:(num1: number, num2: number)=>number = sum1; 
interface Isum {
    (num1: number, num2: number): number
}
let mysum1: Isum = sum1;
let haha: number | string // 联合类型,在没赋值之前只能访问共同的方法、属性
haha.length // number 没有length 属性
haha.toString()
haha.valueOf()
haha = 'string'
haha = 123
// 直接 let a: object; 没有什么意义
let character: { name: string, age: number } // 对象 object
character = {
    name: '张三',
    age: 18
} // 属性必须在类型 { name: string; age: number; } 中
// 直接 let a: object; 没有什么意义
let character: { name: string, age: number } // 对象 object
character = {
    name: '张三',
    age: 18
} // 属性必须在类型 { name: string; age: number; } 中

2 断言 type inference

function getLen(params: number | string): number{
    // 使用 as 断言,params 可以是 number 或 string 类型,断言为 string 才能有 length 属性
    const str = params as string 
    /**
     * 或者
     * const str = <string>params; // 使用 <类型> 断言,功能同上面的 as
     */
    if(str.length){
        return str.length
    }
    return str.toString().length
}

3 类型守卫 type guard

// 遇到联合类型的时候,使用 类型守卫可以 缩小范围
function getLen2(params: number | string): number{
    if(typeof params === 'string'){
        return params.length
    }
    return params.toString().length
}

// 类型守卫 除了 typeof 之外 ,还有 instanceof、 in

4 类 class

  • Public: 修饰的属性或方法是共有的 在 任何地方 都能访问
  • Private: 修饰的属性或方法是私有的 只有 本类 中访问
  • Protected: 修饰的属性或方法是受保护的 在 本类 和 子类中 能够访问
class Parent {
    static species: string = 'human' // 使用 static 修饰的属性是通过 类 去访问
    static readonly username: string = '小明' // 给属性添加上 readonly 就能保证该属性只读,不能修改,如果存在 static 修饰符,写在其后
    public name: string
    private money: number
    protected age: number
    constructor(name: string){
        this.name = name
    }
    eat(){
        console.log(`${this.name}在吃饭`);
    }
}
let zs = new Parent('张三');
zs.eat()

class Son extends Parent {
    private son_money: number
    private son_age: number
    constructor(name: string){
        super(name)
        this.son_money = super.money // 错误,money 为私有属性,只能在类 Parent 中访问
        this.son_age = super.age // 类 Parent 的 age 属性可被子类访问
    }
}
console.log(Parent.species);
Parent.username = '小红' // 错误的,username 为只读属性,不可修改

上面的 name money age 属性都是通过 实例 去访问的

使用 static 修饰的属性是通过 类 去访问,是每个实例共有的

同样 static 可以修饰 方法,用 static 修饰的方法称为 类方法,可以使用类直接调用

TS 新增的抽象类,不希望直接使用该类创建实例 (不能被new) 把它设置为抽象类,让它不能被实例化,只能被继承,

// 抽象类 abstract : 在 class 前面 添加 abstract 修饰符,
// 举个例子:一个动物的抽象类,有个叫的方法,不可能 每个动物的叫声一样吧,我们可以把它设置为抽象方法,具体功能子类进行实现(该怎么叫就由子类来写)
abstract class Animal {
    public name: string
    constructor(name: string){
        this.name = name
    }
    abstract barking(): void // 抽象方法,没有方法体
}
class Cat extends Animal {
    constructor(name: string){
        super(name)
    }
    barking(){
        console.log('喵喵喵~');
    }
}

5 接口 interface

接口: 为了解决 继承 的困境(一个类只能继承另一个类不能实现多继承)

还有一种情况,人能够洗衣服,洗衣机也能洗衣服,洗衣机和人找不到一个共同的父类,我们可以把洗衣服这个功能抽离出来写成接口,人 和 洗衣机 去实现这个接口就行

可以用 implements 来实现接口

// 衣服接口
interface Clothes {
    washClothes(): void
}
// 人类
class Person implements Clothes {
    washClothes(){
        console.log('手洗');
    }
}
// 洗衣机类
class wMachine implements Clothes {
    washClothes(){
        console.log('机洗');
    }
}

接口多实现

// 衣服接口
interface Clothes {
    washClothes(): void
}
// 吃接口
interface Eat {
    eatMeal(): void
}
// 人类
class Person implements Clothes,Eat {
    washClothes(){
        console.log('手洗');
    }
    eatMeal(){
        console.log('人在吃');
    }
}

接口之前可以继承

// 衣服接口
interface Clothes {
    washClothes(): void
}
// 吃接口
interface Eat extends Clothes {
    eatMeal(): void
}
// 人类
class Person implements Eat {
    washClothes(){
        console.log('手洗');
    }
    eatMeal(){
        console.log('人在吃');
    }
}

6 枚举 enum

常量是在项目中经常使用,虽然 const 可以声明常量,但是有的常量取值是在一个范围里的,这里我们就需要使用 enum 来进行处理

数字枚举

// 枚举星期
enum Week {
    Monday, Tuesday, Wednesday, Thurday, Friday, Saturday, Sunday
}
// 默认从 0 开始
console.log(Week.Monday); // 0
console.log(Week.Tuesday); // 1
console.log(Week.Wednesday); // 2
console.log(Week.Thurday); // 3
// 通通过下标获取
console.log(Week[0]); // Monday
console.log(Week[1]); // Tuesday
console.log(Week[2]); // Wednesday
console.log(Week[3]); // Thurday

// 修改枚举中初始值
enum Week {
    Monday = 4, Tuesday, Wednesday, Thurday, Friday, Saturday, Sunday
}
// 修改之后依次增加
console.log(Week.Monday); // 4
console.log(Week.Tuesday); // 5
console.log(Week.Wednesday); // 6
console.log(Week.Thurday); // 7

字符串枚举

enum Week {
    Monday = 'Monday', 
    Tuesday = 'Tuesday', 
    Wednesday = 'Wednesday', 
    Thurday = 'Thurday', 
    Friday = 'Friday', 
    Saturday = 'Saturday', 
    Sunday = 'Sunday',
}
// 获取一个值后 对 week 枚举的值进行匹配
const vale = 'Tuesday'
if(vale === Week.Tuesday){
    console.log('匹配成功');
}else{
    throw new Error('匹配失败');
}

常量枚举

在 enum 前面添加一个 const 即可,它提高了性能 为什么呢?把上面字符串枚举编译成 js 例子,和 常量枚举编译 贴出来对比一下

/******************** 字符串枚举 ***********************/
// 枚举星期
var Week;
(function (Week) {
    Week["Monday"] = "Monday";
    Week["Tuesday"] = "Tuesday";
    Week["Wednesday"] = "Wednesday";
    Week["Thurday"] = "Thurday";
    Week["Friday"] = "Friday";
    Week["Saturday"] = "Saturday";
    Week["Sunday"] = "Sunday";
})(Week || (Week = {}))
// 获得一个值后 对 week 枚举的值进行匹配
var vale = 'Tuesday';
if (vale === Week.Tuesday) {
    console.log('匹配成功');
}
else {
    throw new Error('匹配失败');
}

/******************** 常量枚举 ***********************/
// 获得一个值后 对 week 枚举的值进行匹配
var vale = 'Tuesday';
if (vale === 'Tuesday' /* Tuesday */) {
    console.log('匹配成功');
}
else {
    throw new Error('匹配失败');
}

常量枚举直接找出 Week.Tuesday 上面一截都没了

7 泛型

泛型就像一个占位符一个变量,在使用的时候我们可以将定义好的类型像参数一样传入,原封不动的输出

// 这里 T 是相当于一个占位符,在方法(变量,接口....等等)后面添加 <T>
function getValue<T>(params: T): T {
    return params
}
// 在使用 getValue这个方法的时候 只需要在 实参 规定好类型,编译器能够知道我们的参数类型,并将它们赋值给 T
function getValue<123>(params: 123): 123
getValue(123)

// 多个参数
function getValue<T, U>(params: [T, U]): [T, U] {
    return params
}
// 在使用的时候,聪明的就判断出 传入的类型,并修改了 T,U,真的很方便
function getValue<number, string>(params: [number, string]): [number, string]
getValue([123, '123'])

可以使用 interface 来约束 泛型

T 后面 extends Ilen ,定义 Ilen 里面代码表示,T 必须要有 length 属性,这样在 方法里面调用 params.length 就不会报错

interface ILen {
    length: number,
}
function getLength<T extends ILen>(patams: T):number {
    return params.length
}
getLength('str') // √
getLength([]) // √
getLength(123) // ×

在 类 使用泛型

class Kitchen<T>{
    makeCake(name: T){
        console.log('厨房制作了'+name+'蛋糕');
    }
}
let kit = new Kitchen<string>()
kit.makeCake('草莓') // √
kit.makeCake(12345) // ×

在 接口 使用泛型

interface keyValue<T, U>{
    key: T
    value: U
}
let test: keyValue<string, number> = { key: 'test', value: 12345 }
let test2: keyValue<number, string> = { key: 12345, value: 'str' }

在 数组 使用泛型

// 一开始我们都这样定义类型
let myarr: number[] = [1, 2, 3, 4, 5]
// 使用泛型
let myarr1: Array<number> = [1, 2, 3, 4, 5]
let myarr2: Array<string> = ['1', '2', '3', '4', '5']

8 类型别名

使用 type 给类型取别名

type test = number
let count_number: test = 123

type test2 = number | string
let str: test2 = '1234'
let str2: test2 = 12345

9 交叉类型

& 进行连接,把类型都组合起来,变量赋值必须满足 交叉类型

interface Itest {
    key: string
}
type test4 = Itest & { value: string }
let mytest: test4 = { key: '哈哈', value: '哈哈' }