Loading...
墨滴

盘胧

2021/07/31  阅读:42  主题:绿意

typecript-类型

重塑类型思维

​ 接口的提供方将被迫去思考API的边界,从代码的编写者蜕变为代码的设计者,

​ typescript是javascript的超集,将动态弱类型的js,增强了类型检查束缚,让前端开发的路上走的更稳,更远。

typescript

静态强类型

编译器:tsc: 使用tsc编译一个ts文件

基本类型

基于ES6新增了几种数据类型

  • Boolean
  • Number
  • String
  • Array
  • Function
  • Object
  • Symbol
  • undefined
  • null
  • void
  • any
  • never
  • 元组
  • 枚举
  • 高级类型

类型注解

相当于强类型语言中的类型声明

语法:(变量/函数):type

原始类型示例

let bool:boolean = true
let num:number = 123
let str:string = "abc"

数组类型示例

let arr1:number[] = [123]  // 表示只有整形数字的数组
let arr2:Array<number> = [123]
let arr3:Array<number|string> = [1,2,3,"abc"// 表示联合类型的数组

元组类型:特殊的数组

let tuple:[numberstringboolean] = [234"dfd"true]  // 其实是固定类型的数组

函数类型

let add = (x:number,y:number) => x + y // 定义了函数
let compute:(x:number,y:number) => number  // 定义了函数但是没有具体实现
/*具体实现*/
compute = (a, b) => a + b

对象类型

let obj:object = {x:1,y:2// 这种定义方法不可修改
let obj2:{x:number,y:number} = {x:1,y:2// 可修改
/*修改obj属性*/
obj2.x = 3

symbol 类型:具有唯一的值

let s1:symbol = Symbol()
let s2 = Symbol()
// 此时s1 和 s2 是不相等的   

undefined,null类型

官方说明:undefined和null是任何类型的子类型,是可以赋值给其他类型的

undefined并不是保留字, 可以自定义覆盖全局的undefined

let un:undefined = undefined  // 此时不能赋值其他类型了,只能赋值undefined本身
let nu:null = null

void类型:没有任何返回值的类型

let noReturn = () => {}

any类型:如果不指定类型,就默认是any类型

let x:any // 可以任意赋值

never类型 :永远不会有返回值的类型

let error = () => {    throw new Error('never')}  // 抛出异常,就没有返回值let endless = () => {    while(true) {}} //  死循环类型,也永远没有返回值

枚举类型:一组有名字的常量集合——只读

一定程度上解决了可读性和可维护性的问题

数字枚举:进行了反向映射!~
enum Role {    Reporter,    Developer,    Maintainer,    Owner,    Guest}console.log(Reporter)  // 0console.log(Developer)  // 1console.log(Maintaine)  // 2console.log(Owner)  // 3console.log(Guest)  // 4// 自定义初始值enum Role {    Reporter = 1,    Developer,    Maintainer,    Owner,    Guest}console.log(Reporter)  // 1console.log(Developer)  // 2console.log(Maintaine)  // 3console.log(Owner)  // 4console.log(Guest)  // 5

我们既可以用‘成员’ 进项索引,也可以用‘值’进项索引

可以在ts的官方网站使用playground进行查看,enum是如何进项实现的:

enum Role {    Reporter = 1,    Developer,    Maintainer,    Owner,    Guest}*/var Role;(function (Role) {    Role[Role["Reporter"] = 1] = "Reporter";    Role[Role["Developer"] = 2] = "Developer";    Role[Role["Maintainer"] = 3] = "Maintainer";    Role[Role["Owner"] = 4] = "Owner";    Role[Role["Guest"] = 5] = "Guest";})(Role || (Role = {}));

可以看到,Role["Reporter"] = 1枚举成员的名称被作为了key,枚举成员的值被作为了value

Role[Role["Reporter"] = 1] = "Reporter";然后返回的value又被作为了key, 成员的名称又被作为了value

以上这种方法叫叫做方向映射

字符串枚举:没有进项反向映射
enum Message {    Success = "恭喜你,成功了",    Fail = "抱歉哦, 失败了"}

看下是如何实现的:playground

"use strict";var Message;(function (Message{    Message["Success"] = "\u606D\u559C\u4F60\uFF0C\u6210\u529F\u4E86";    Message["Fail"] = "\u62B1\u6B49\u54E6\uFF0C \u5931\u8D25\u4E86";})(Message || (Message = {}));

可以看到, 并没有像数字枚举一样进行反向映射

异构枚举,将字符串枚举和数字枚举混用

并不建议使用 ,容易引起混淆

enum Answer {    N,    Y = 'Yes'}

枚举成员分为几种?

enum Char {
    // 常量枚举会在编译时候出结果
    a,  // 属于常量枚举  const
    b = Char.a, // 属于常量枚举  const  对已有成员的引用
    c = 1 + 3,  // 属于常量枚举  const  常量表达式
    // 非常量枚举,编译时候不会被进行,执行的时候才会进行
    d = Math.random(), // 非常量枚举  属于conputed, 需要被计算 
    e = '123'.length
    //  非常量枚举类型的后面出现的枚举,无论是否常量枚举,都要有初始值!~
}

看下编译结果 playground

"use strict";
var Char;
(function (Char{
    Char[Char["a"] = 0] = "a";
    Char[Char["b"] = 0] = "b";
    Char[Char["c"] = 4] = "c";
    Char[Char["d"] = Math.random()] = "d";
    Char[Char["e"] = '123'.length] = "e";
})(Char || (Char = {}));

常量的已经被编译出了结果,非常量的没有出结果,仍然保留表达式

常量枚举:使用const声明的枚举

特性:编译阶段会被移除,在playground上看不到任何代码

当我们只需要一个对象的值的时候,我们可以用常量枚举,可以减少编译时候的代码

const enum Month {
    Jan,
    Feb,
    Mar
}

接口:Interface

直接看定义 interface

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)

鸭子类型

很多时候后端传过来的数据字段并不一定符合我们定义,有多余的字段

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

如上,多了有sex,但是ts这样也不会报错!~

但是:如果直接将result的值丢进去,那就会报错:

render({
    data:[
        {id:1,name:"A",sex:'male'},  // 这里会提示错误,编译会失败
        {id:2,name:"B"}
    ]
})

绕过类型检查:进行类型断言,又可以不报错

明确的告诉编译器,我们传入的参数就是Result

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"}
    ]
})

索引签名,也可以绕过 检查报错

interface  List {
    readonly id:number// 只读属性
    name:string,
    [x:string]:any,
    age?:number // 可有可无
}

不确定一个接口中有多少属性:使用可索引类型的接口

既可以使用数字,也可以使用字符串

// 用任意的数字去索引stringarray,都会得到一个string, 相当于声明了一个字符串类型的数组
interface StringArray {
    [index: number]: string
}
let char:StringArray = ["A""B"]

// 用字符串去索引一个接口
//  用任意的字符串去索引Names, 得到的结果都是string
interface Names {
    [x:string]:string//  这样声明之后,就不能声明number类型的成员
    // y:number  这样是不被允许的
    [z: number]:string // 这样我们既可以用数字,也可以用字符串索引Names
    // 需要注意的是:数字索引的返回值,一定要是字符串类型索引的子类型,因为js会进行类型转换,将number转换为string,这样会保持类型的兼容性
    // [z:number]:number   // 这样就和string不兼容了, 如果要兼容,可以将 [x:string]:string 改为  [x:string]:any
}

接口定义函数

我们可以用一个变量定义函数

let add:(x:number,y:number) => number

我们可以用接口定义它 => 等价变量定义

interface Add {
    (x:number,y:number):number
}

我们还可以使用类型别名定义

type Add = {x:number,ty:number} => number
let add:Add = (x,y) => a + b

混合类型接口

解释:既可以定义一个函数,也可以像对象一样拥有属性和方法

interface Lib {
    ():void;  // 首先定义一个函数,假设没有返回值和参数
 version:string;
 doSomething(): viod;
}

定义好了接口,我们如何进行实现?

let lib:Lib = (() => {}as Lib  // 进行断言
lib.version = '1.0'
lib.doSomething = () =>
 {}

上面这样定义的lib属于暴露全局的,且是单列,如果像创建多个,可以进行‘域’限定:函数封装~

function getLib {
    let lib:Lib = (() => {}as Lib  // 进行断言
 lib.version = '1.0'
 lib.doSomething = () =>
 {}
    return lib
}
lib1 = getLib();
lib1.doSomething();
lib2 = getLib();
lib3 = getLib();
lib4 = getLib();

总结

到这ts的基本类型就差不多完成了,剩下的:函数,抽象类,多态,泛型,索引类型,映射类型,条件类型,交叉类型,联合类型等,后面再说

盘胧

2021/07/31  阅读:42  主题:绿意

作者介绍

盘胧