TypeScript 入门 —— 基础类型、变量声明、接口与类

166 阅读7分钟

TS介绍

TypeScript 起源于使用JavaScript开发的大型项目。由于JavaScript语言本身的局限性,难以胜任大型项目的开发和维护。因此微软开发了TypeScript ,使得其能够胜任大型项目的开发。

TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以运行在TypeScript环境中。TypeScript是为大型应用的开发而设计,并且可以编译为JavaScript。

它写起来很像Java

TypeScript 中的数据类型

  • JavaScript中已有的数据类型: NumberStringBooleanNullundefinedObjectSymbolBigint
  • TS拓展出的数据类型
  • any: 允许任何类型
  • unknown: 确保使用此类型的人声明类型是什么
  • never: 该类型不可能出现
  • void: 返回 undefined 或者 无返回值
  • tuple: 元组
  • enum: 枚举

下面将一一举例介绍这些TS中新增的数据类型

任何类型 - any

全用any就被戏称为AnyScript

这种变量类型一般用于标识一个动态的数据,即实际运行时该数据的数据类型是编写程序时无法划定范围的,或者图省事...,也可能该数据类型来源于第三方库,使用any标识他们可以在编译阶段让他们通过ts类型检查器的检查,从而顺利被编译。any不能赋值给never

let notSure: anyl

notSure = 'hello Ts'
notSure - true
notSure = 6

let notSureList: any[] = [ 'hello', false ]

Object的区别: Object类型的变量允许你给他赋任意值, 但是不可以在这个变量上调用Object原型上具有的方法

无类型 - void

void类型的变量只能被复制undefined或者null

let t2: void = undefined
let t3: void = null

空和未定义 - null & undefined

默认情况下nullundefined是所有类型的子类型。使用--strictNullChecks后,nullundefined只能复制给他们各自

永不存在的值的类型 - Never

永不存在的值的情景:

  • 总是会抛出异常
  • 根本就不会有返回值的函数表达式,例如无线循环
  • 箭头函数表达式的返回值类型
  • 变量被永不为真的类型保护所约束时

never是任何类型的子类型, 可以赋值给任何类型。但never没有子类型,也没有可以赋值给never的类型,包括any

示例:

// 总是会抛出异常
function getErr(message: string): never{
    throw new Error(message)
}

//无限循环
function infinitLoop(): never{
    while(true){
        console.log('无限循环')
    }
}

类型断言

在类型和值起冲突,但你知道这个变量的确切类型并不是定义它时候的类型时——通常用在any类型的变量上,使用断言告诉类型检查器这里没有问题,可以通过编译。

let myA: any = "hello world"
  • 断言的第一种语法: <dataType>data
let strLength: number = ( <string>myA ).length
  • 断言的第二种语法: data as datatypeJSX中,只可以使用这种断言
let strLength: number = ( myA as string ).length

元组 - tuple

其中的元素的数据类型不必都相同

let t1: [string, number]
t1 = ['theOne', 66]

如果你要访问一个越界的元素,越界元素的值属于定义时给出的类型,那么ts会使用联合类型替代,否则报Error

// 借用上面的t1
t1[2] = 'hello world' // 正常
t1[3] = true  //Error, 定义元组时候没有允许Boolean类型的数据

枚举 - enum

格式:

enum name { value1, value2, ... }

// 给枚举元素编号, 让枚举类型中的元素的索引从1开始
enum name2 { value3 = 1, value4, ... }

// 自定义编号
enum name3 = { value5 = 1, value6 = 2, value7 = 6, ... }  
  
// 使用
let test: name = name.value2

// 通过编号映射得到枚举的值
let test2: string = name3[2]

声明变量 - let、var、const

格式参考:

let name: dataType = value

//for example
let name: string = '暮商'
name = '雩风'

声明数组:

let arr: number[] = [ 1, 2, 3 ]

声明泛型:

let list: Array<myE> = []

解构赋值与扩展运算符

参照ES6

指定类型的属性重命名

const { data, code }: { data: object, code: number } = res

// 属性为undefined时的默认值, size为undefined时,取默认值20
const { code, size=20 }: { code: number, size?: number } = res

扩展运算符的浅拷贝、深拷贝问题

对被扩展数组或对象中第一级基础数据类型的拷贝是深拷贝,修改他们的值不会彼此影响。而对被扩展数组或者对象中的引用数据类型(比如常见的Array、Object)的拷贝是浅拷贝,即地址的拷贝,修改这里面的值会相互影响

扩展运算符的限制

  • 会丢失被展开对象实例中的方法

  • 展开顺序是从左到右,这意味着后展开的属性若与前面同名,则会覆盖前面同名属性的值

接口

官方文档里这样写到:在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

这里的契约是指定义之后,对应变量的类型就被定义时赋予的类型严格限制,而不能像js中那样自由的改变

构建类型

TS中构建类型可以使用interface接口或者type类型。一般来说,普遍使用interface,而当需要特定功能时使用type

interface 构建类型

interface: 严格定义对象中属性的类型,他要求某个接口中必须含有定义的属性且其类型为定义的类型

// 声明一个接口,并描述其中数据的类型
interface loginForm {
    account: string,
    passwords: string
}

这里定义了一个对象的接口,我们可以使用这个接口去声明一个符合该接口的对象,请注意,声明时候赋值应当符合接口给出的限制,否则TS会警告。另外,声明的对象可以包含多个属性,但必须含有account且其类型为string;必须含有passwords且其类型为string

声明的语法如下:

// 格式
const objName: interfaceType = {
    key1: value1,
    key2: value2
}

// 实例
const form: User = {
    account:  '',
    passwords: ''
}

可选值

如果接口中某个属性是可能存在的,那么使用?将其标注为可选属性。例如下面的例子,age属性是可选的,你可以在实例中不声明age,也可以在函数中调用此接口时不传入age,但若用到age则其类型必须为number

interface example{
    name: string,
    sex: string,
    age?: number // 可选属性
}

只读属性

使用readonly标识, 顾名思义,声明时赋值后,只可访问其值而不可修改

interface example{
    name: string,
    sex: string,
    readonly mother: string
}

TS提供了ReadonlyArray<T>类型, 它表明数组中的值是只读的, 它也不可以被直接赋值给一个普通数组,但可以使用断言将其赋值给一个普通数组

normalArr = readonlyArr as number[]

索引签名 - 通过索引得到的类型

描述通过索引得到的属性的返回值类型,比如数组中元素类型

 interface example{
     [index: number]: string
 }
 
 let textArr: example;
 textArr = ["e1", "e2"]
 
 let str: string = textArr[0]

TS支持使用stringnumber两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型

函数类型

定义函数接口

接口中的变量名不需要和实际传入的变量名严格匹配

// 格式
interface myFunction{
    ( param1: dataType, param2: dataType ): returnType
}

//示例, 函数接收参数account、passwords, 返回值为true或false
interface checkLogin{
    ( account: string, passwords: string ): boolean
}

使用接口

let check: checkLogin;
// 也可以省略 :boolean,因为接口中已经定义返回值类型
check = function(acnt:string, pwds: string):boolean{
    return acnt!='root' && pwds!='admin'
}

type构建类型

联合

可以声明类型为多种类型之一, 语法如下

type name = value1 | value2 | ...

// for example
type buttontype = 'error' | 'success' | 'info' | 'warning'
type flag = 30 | 50

function getKey(obj: Object | Array | String[]){}

typeof: 可以判断变量类型,语法同js: typeof obj === 'boolean'

泛型

类似Java中的泛型,如果有Java基础,那么以下示例应该是容易理解的

type permissionsList = Array<string>
type userList = Array<{ userName: string }>

自定义使用泛型的类型

interface myE<Type> {
   name: string,
   add: (param: Type) => void
   get: () => Type
}
   
const test: myE<number>;
test.add(66)
test.get()

类的接口

// 类的接口    
interface timeClass{
    time: Date;
}
    
// 类实现接口
class clock implements timeClass{
    time: Date;
    
    // 构造器
    constructor(hh: number, mm: number){  };
    
    //setter
    setTime(dd: Date){
        this.time = dd
    }
}

类的静态部分和实例部分