初学typescript(一)

前言

ts有多火大家也知道,惭愧的是,我现在还对它不熟悉,今天就开始学习一下吧。

typescript的数据类型

相比于ES6的数据类型来说,typescript拥有了新的几种数据类型,它们分别是voidanynever元组枚举以及其它的一些高级类型。
typescript中,我们定义一个变量时,需要指定它的类型(当然不指定的话ts也会进行类型推断),它的写法如下:

//原始类型
let bool:boolean = true
let num:number = 123
let str:string = 'abc'

// 数组
let arr1: number[] = [1, 2, 3]
let arr2: Array<number | string> = [1, 2, 3, '2']

// 元组 可以为元组添加数据,但无法访问
let tuple: [number, string] = [0, '1']
// tuple.push(2)
// console.log(tuple)
// tuple(2)

// 函数
let add = (x: number, y:number): number => x + y
let compute: (x: number, y: number) => number
compute = (a, b) => a + b

// 对象
let obj: {x: number, y: number} = {x: 1, y: 2}
obj.x = 3

// symbol
let s1: symbol = Symbol()
let s2 = Symbol()
console.log(s1 === s2)

// undefined, null
let un: undefined = undefined
let nu: null = null

// void 没有返回值
let noReturn = () => {}

// any
let x

// never 永远不会有返回值的类型
let error = () => {
    throw new Error('error')
}

let endless = () => {
    while(true){}
}

枚举

一组有名字的常量的集合。
你可以把它当成一个通讯录,在拨打电话的时候只需要记住人名就可以了,而不需要去记得它的电话号码,而且电话号码是经常变化的,而人名基本不会发生变化。

枚举

为什么使用枚举?

我们可以使用枚举定义一些带名字的常量,也可以清晰地表达意图或创建一组有区别的用例。
如下例子,我们写了一个对角色进行判断的方法:

function initByRole (role){
    if(role === 1 || role === 2){
        // do sth
    }else if(role === 3 || role === 4){
        // do sth
    }else if(role === 5){
        // do sth
    }else {
        // do sth
    }
}

但是它是存在问题的:

  1. 可读性差:如果没有文档,我们很难记住这些数字的含义
  2. 可维护性差: 硬编码,牵一发而动全身

如果我们使用枚举呢?我们可以这样写:

const enum RoleEnum{
    Reporter = 1,
    Developer,
    Maintainer,
    Owner,
    Guest
}

function initByRole(role: RoleEnum) {
    if (role === RoleEnum.Reporter || role === RoleEnum.Developer) {
        // do sth
    } else if (role === RoleEnum.Maintainer || role === RoleEnum.Owner) {
        // do sth
    } else if (role === RoleEnum.Guest) {
        // do sth
    } else {
        // do sth
    }
}

枚举有哪些?

  • 数字枚举
enum Role {
    Reporter,
    Developer,
    Maintainer,
    Owner,
    Guest
}
  • 字符串枚举
enum Message {
    Success = '恭喜你,成功了',
    Fail = '抱歉,失败了'
}
  • 异构枚举(不推荐)
    从技术的角度来说,枚举可以混合字符串和数字成员:
enum Answer {
    N,
    Y = 'yes'
}
  • 枚举成员
enum Char {
    // 常量成员
    a,
    b = Char.a,
    c = 1 + 2,
    // 计算成员
    // computed不会在编译阶段计算,会被保留到执行阶段,在这后面定义的枚举成员必须有初始值才不会报错
    d = Math.random(),
    e = '123'.length
}
  • 常量枚举
const enum Month {
    Jan,
    Feb,
    Mar
}

let month = [Month.Jan, Month.Feb, Month.Mar] //var month = [0 /* Jan */, 1 /* Feb */, 2 /* Mar */];
  • 有时枚举和枚举成员都可以作为一种类型存在
enum E { a, b}
enum F { a = 0, b = 1}
enum G { a = 'apple', b = 'banana' }

//枚举作为了类型
let e: E = 3
let f: F = 3

//枚举成员作为了类型
let e1: E.a
let e2: E.b
let e3: E.a

let g1: G
let g2: G.a

接口

这个接口可不是我们平时写业务代码时请求后台数据的那个接口啊,那这个接口是什么呢?

接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。

简单的说呢,就是它定义了一些属性、函数、可索引和类的所需要遵守的规范,然后自己就相当于变成了一种类型,去对值所具有的结构进行类型检查。

对象类型接口

interface List {
    id: number;
    name: string;
}

interface Result {
    data: List[]
}

function render(result: Result){
    result.data.forEach((value) => {
        console.log(value.id, value.name)
    })
}

let result = {
    data: [
        {
            id: 1,
            name: 'A'
        },
        {
            id: 2,
            name: 'B'
        }
    ]
}

render(result)

这是一个打印数组对象idname的方法,没毛病,如果我们此时修改一下result的结构,增加一个变量:

let result = {
    data: [
        {
            id: 1,
            name: 'A',
            sex: 'male'
        },
        {
            id: 2,
            name: 'B'
        }
    ]
}

ts不会报错,因为TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。就是说,如果一只鸟,看起来像鸭子,游起来像鸭子,叫起来像鸭子,那这只鸟就可以被认为是鸭子。
我们只要传入的对象满足接口的必要条件,就是被允许的,即使传入了多余的字段,也可以通过类型检查。但也有一个例外,如果我们直接传入对象字面量,ts就会对额外的字段进行检查:

//有警告
render({
    data: [
        {
            id: 1,
            name: 'A',
            sex: 'male'
        },
        {
            id: 2,
            name: 'B'
        }
    ]
})

想绕过这种检查的话,有三种方法:

  1. 像之前那样,用一个变量存储起来
  2. 使用类型断言
render({
    data: [
        {
            id: 1,
            name: 'A',
            sex: 'male'
        },
        {
            id: 2,
            name: 'B'
        }
    ]
} as Result)
// 也可以这样,两者等价,但推荐上面那种,因为下面这种在react中可能导致歧义
render(<Result>{
    data: [
        {
            id: 1,
            name: 'A',
            sex: 'male'
        },
        {
            id: 2,
            name: 'B'
        }
    ]
})
  1. 字符串索引签名
interface List {
    readonly id: number;
    name: string;
    [x: string]: any
}

可索引类型接口

// 用任意的数字去索引StringArray,得到的都是string
interface StringArray {
    [index: number]: string
}
let char: StringArray = ['A', 'B']
interface Names {
    [x: string]: string;
    // y: number 不能再声明number类型成员
    [z: number]: string //数字索引签名的返回值一定要是字符串索引签名返回值的子类型,因为会进行类型转换,number转换为string,这样可以保持类型的兼容
}
// => 所以数字索引如果返回数字,字符串索引返回就需要更大范围
interface Names2 {
    [x: string]: any;
    [z: number]: number
}

函数类型接口

接口能够描述JavaScript中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外,接口也可以描述函数类型。

interface Lib {
    (): void;
    version: string;
    doSomething(): void;
}

function getLib() {
    let lib: Lib = (() => { }) as Lib
    lib.version = '1.0'
    lib.doSomething = () => { }

    return lib
}

let lib1 = getLib()
lib1()
lib1.doSomething()

ts还是要在实战中多多使用才能融会贯通,目前只是熟悉,大致了解ts的写法与基本用法,慢慢来。

posted @ 2020-11-08 14:01  来亦何哀  阅读(260)  评论(4编辑  收藏  举报