【Typescript】- Typescript基础

814 阅读7分钟

「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

前言

我们先回顾一波 Typescript预备知识,做好准备工作,紧接着就可以开始学习 Typescript 基础知识啦。

基础知识

TS中的类型层次

image.png 由上图可知,顶层Top是全集,底层Bottom是空集。自上而下来看,上方的类型是下方类型的父集。

划重点:下层类型的值可以赋给上层类型的变/常量。
因为,unknown类型的变/常量可以指向任何类型的值。所以不存在never类型的变量(never是空集)。

对比不同语言的 Top/Bottom Type

语言Top TypeBottom Type
TypeScriptunknownnever
PHPmixednever
Pythonobjecttyping.NoReturn
KotlinAny?Nothing
ScalaAny?Nothing
Javajava.lang.Object
C#System.Object
Gointerface{}
PerlUniversal
C++std::any
Cvoid
Object Pascal(Delphi)TObject

Any

  1. any比较特殊,其实它即是Top Type 又是 Bottom Type。

也就是说:any类型的变量/常量(任何其他类型的变量常量,可以互相赋值)。

但any类型是不安全的、无语言服务的,所以应该尽量避免使用。

  1. any具有传染性:它会使它所触及的地方变得不安全。

所以TS在3.0引入了类型安全的unknown类型作为Top Type。

  1. any会隐藏bug

因为没有类型信息,即便错误使用,也没有任何报错。

比如字符串birthDate没有getTime()方法,但不会报错。

function calculateAge(birthday: Date){
    var ageDate = new Date(Date.now() - birthday.getTime());
    return Math.abs(ageDate.getUTCFullYear() - 1970);
}
let birthDate: any = '1999-09-09'
  1. any会隐藏代码设计细节:丢失了对数据类型的设计。

既然 any 类型会带来上述这么多问题,我们可以在项目的 tsconfig 中开启 strict 模式或禁止隐式 any。

image.png

unknown

let a:unknown = 30 //typeof a ==unknown
//unknown 类型必须显示注解  TS不会把任何值推导为unknown

let b1 = a > 123 // Error TS2571:Object is of type 'unknown'
let b2 = a < 123 //Error TS2571
let b3 = a ===123 // typeof b == boolean
// unknown类型只能进行等于和不等于比较

let c = a + 10 // Error TS2571

if(typeof a ==='number'){
    let d = a + 10  //typeof d == number
    // 只有类型收窄后才能进行相应的运算或函数调用
}

划重点: 如果无法预知类型,不要用any,用unknown,收窄类型后再使用。

布尔类型(boolean)

布尔类型,只有两个元素 true 和 false。

let a = true //typeof a = boolean
var b = false // typeof b = boolean
const c = true //typeof c = true
//let var 变量会被拓展成 Boolean类型  const常量就是对应的字面量类型

let d: boolean = true //typeof d = boolean
let e: boolean = true //typeof e = boolean

//Error TS2322:Type 'false' is not assignable to type 'true'
let f :true = false

let g: true | false = true //let g :boolean
//有一点值得注意的是,truefalse的联合类型,会被反推回boolean类型

number类型

number 类型包含:整数,浮点数,+_Infinity(正负无穷),NaN。

let a = 1234     //typeof a = number
var b = Infinity * 0.1   //typeof b = number
const PI = 3.14   //typeof PI = 3.14
const nan = NaN   //typeof nan = number

let c: number = 2.34   //typeof c = number
let d: 4.56 = 4.56     //typeof d = 4.56

//Error TS2322:Type '10' is not assignable to type '4.56'
let e: 4.56 = 10

bigint 类型

bigint 是新引入的类型,可以表示任意大小的整数,number范围[-(2^53-1), 2^53-1] bigint字面量是在数字后面加小写“n”。bigint支持 加+,减-,乘*,除/, 求余%,幂**。

let a = 1234n  // typeof a = bigint
    
const b 5678n  // typeof b = 5678n
var c = a + b  
const hugeString = BigInt("900789739482")  //typeof hugeString = bigint
const hegeHex = BigInt("0*1fffffffffff")  // typeof hugeHex = bigint

const rounded = 5n/2n  // typeof rounded = bigint  rounded = 2n

//Error : Operator '+' cannot be applied to types 'bigint' and '123'
let d = a + 123 
let e = a + BigInt(123)
let f = Number(a) + 123

划重点:bigint 不能和number混合运算,需要显示转换。

字符串类型(string)

let a = "hello"   // typeof a = string
let b = "world"   // typeof b = string
const c = "!"		// typeof c = "!"

type Dir = "north" | "south" | "east" | "west"
type Direction = Dir | Capitaliza<Dir> | Uppercase<Dir>
// 联合
// type Direction = Dir | "North" | "South" | "East" | "West" |"NORTH" | "SOUTH" | "EAST" | "WEST"

function echoDir(dir: Direction) {consolo.log(dir)}
// dir必须是Direction类型

echoDir('north')
echoDir('NORTH')
echoDir('North')
echoDir('NoRth')  // Error


type RT = XMLHttpRequestResponseType
// 字符串字面量联合
// 常用区分数据类型
//type XMLHttpRequestResponseType = ""  | "arraybuffer" | "blob" | "document" | "json" | "text"

符号类型(symbol)

symbol

符号类型symbol,是ES2015引入的新的语言特性。

import { Equal } form '@type-challenges/unils'

let a = Symbol('a')  // typeof a = symbol
var a1 = Symbol('a') // typeof a1 = symbol
//let var 声明的变量推导成symbol类型


let a2 = unique symbol = a1 //Error
//unique symbol必须是const常量

const b1x = Symbol('b') //"unique symbol b1x"
// const 常量推导成unique symbol,也可以显示注解成unique symbol

type X = Equal<typeof b1, typeof b1x>  // false

划重点:unique symbol不是一个类型,而是一组类型。比如上面代码,unique symbol b1 和unique symbol b1x 是两个类型。

unique symbol

const b2 = b1; // typeof b2 = symbol
type R1 = Equal<typeof b2, typeof b1>;// false
type R11 = Equal<ypeof b2, symbol>;// true
console.log(b1 === b2);// true

const b3: typeof b1 = b1;
type R2 = Equal<typeof b3, typeof b1>;// true
console.log(b1 === b3);// true

console.log(Symbol('a') === Symbol('a'));// false
// Symbol的第一个参数是描述, 不是符号名,也不是符号ID。 每次调用Symbol都会返回一个全新的符号,即便描述相同

console.log(Symbol.for('a') === Symbol.for('a'));// true
// Symbol.for在内部维护了一个字典,如果之前没有创建过 对应描述的符号,就返回一个新的符号,如果创建过,就返回已创建的符号。

划重点:将unique symbol 赋值给另一个const时,类型会拓宽为symbol。如果不希望拓宽,需要显示 注解为对应常量的typeof。

对象类型(Object)

我们学习对象类型,可以用对象来类比,从以下2个方面对比:

  1. 定义对象和定义对象类型
  2. 获取对象keys和获取对象类型key

image.png

数组类型(Array)

数组有两种注解方式:

  1. T[]
  2. Array<T>接口泛型
let a = [1,2,3]  //typeof a = number[]
var b = ['a','b'] // typeof b = string[]
let c = [1.'a']    // typeof c = (string | number)[]
const d = [2,'b']  // typeof d = (string | number)[]
//const 数组不会收窄,因为收窄就变成tuple类型了

a.push('red')   // Error :Argument of type 'string' is not assignable
                // to parameter of type 'number'
let e = []      // typeof e = any[]   
// Ts无法判断空数组的类型  只能推断为any[]
e.push(1);
e.push('red')

function buildArray(){
    let e = [];  // typeof e = any []
    e.push(1);
    e.push('red')   // let e:(string | number)[]
    // 当离开作用域时 TS可以分析代码   推断出数组的类型
    
    return e;
}

const myarray = buildArray()   
//Error: Argument of type 'boolean' is not
// assignable to parameter of type 'string|number'
myarray.push(true);
myarray.push(2)         // ok
myarray.push('bule')    // ok

元组(Tuple)

元组是数组的子类型,元组各索引上的元素类型是确定的。

因为元组的创建方法和数组是一样的 所以元素必须显示注解。

let a : [number] = [1] 
let b : [string,string,number] = ['a','b',1]

b = ['c','d','e']   // Error :Type 'string' is not addignable to type 'number'   因为e的类型应该是数字

let c:[number,number?][] [  //支持可选操作符
    [1,2],
    [3.4,5.6],
    [7]
]
type C = typeof c[0] // type C = [number,number?]

type StringTuple = [string,...string[]]   //元组支持rest操作符
// [...string[]] 等价于 string[],但[string, ...string[]]不等价于string[],前者至少包含一个元素

let d :StringTuple = ['a','b','c'];

type NBS = [number,boolean,...string[]]
let list: NBS = [1,false,'a','b','c']

枚举(Tuple)

枚举

枚举本质上是一种映射。会在值空间产生一个包含该映射的对象。

image.png 存在双向映射

  • 未显式赋值的枚举值自动从0自增赋值
  • 显示赋值为整数的枚举 不存在反向映射
  • 显式赋值字符串的枚举不存在反向映射

枚举合并

枚举合并:枚举可以拆分成多段,还可以与同名namespace合并。 image.png

常量枚举

常量枚举不会在值空间创建变量
所有引用常量枚举的地方都被替换为对应的值
(但是可以通过perserveConstEnum编译器选项来控制)

{
    "compilerOptions" :{
        "preserveConstEnum" : true
    }
}

image.png

null,undefined,void,never

我们可以对比 JS、TS,来具体看看null,undefined,void,never:

JS 中:

  • Undefined = {undefiend},应该表示尚未定义, 但是,实际上表示已声明,未赋值
  • Null = {null},表示已声明,值为null或值为空

TS 中:

  • void类型:函数没有显式返回值
  • never类型:函数无法返回

image.png

  1. 在JS,void是个一元操作符: 它执行后面的表达值, 然后无条件返回undefined。
  2. 在ES1.3之前,undefined是无法直接访问的! 只能通过void(0)这种形式得到。ES1.3将 undefined添加到了global object上之后才可以访问。

总结

上述我们学完了 Typescript 的基础知识,大家应该对 ts 的基本类型有了认知,打好基础后,我们才能继续进阶学习哦~