「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」
前言
我们先回顾一波 Typescript预备知识,做好准备工作,紧接着就可以开始学习 Typescript 基础知识啦。
基础知识
TS中的类型层次
由上图可知,顶层Top是全集,底层Bottom是空集。自上而下来看,上方的类型是下方类型的父集。
划重点:下层类型的值可以赋给上层类型的变/常量。
因为,unknown类型的变/常量可以指向任何类型的值。所以不存在never类型的变量(never是空集)。
对比不同语言的 Top/Bottom Type
语言 | Top Type | Bottom Type |
---|---|---|
TypeScript | unknown | never |
PHP | mixed | never |
Python | object | typing.NoReturn |
Kotlin | Any? | Nothing |
Scala | Any? | Nothing |
Java | java.lang.Object | |
C# | System.Object | |
Go | interface{} | |
Perl | Universal | |
C++ | std::any | |
C | void | |
Object Pascal(Delphi) | TObject |
Any
- any比较特殊,其实它即是Top Type 又是 Bottom Type。
也就是说:any类型的变量/常量(任何其他类型的变量常量,可以互相赋值)。
但any类型是不安全的、无语言服务的,所以应该尽量避免使用。
- any具有传染性:它会使它所触及的地方变得不安全。
所以TS在3.0引入了类型安全的unknown类型作为Top Type。
- 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'
- any会隐藏代码设计细节:丢失了对数据类型的设计。
既然 any 类型会带来上述这么多问题,我们可以在项目的 tsconfig 中开启 strict 模式或禁止隐式 any。
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
//有一点值得注意的是,true和false的联合类型,会被反推回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个方面对比:
- 定义对象和定义对象类型
- 获取对象keys和获取对象类型key
数组类型(Array)
数组有两种注解方式:
- T[]
- 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)
枚举
枚举本质上是一种映射。会在值空间产生一个包含该映射的对象。
存在双向映射
- 未显式赋值的枚举值自动从0自增赋值
- 显示赋值为整数的枚举 不存在反向映射
- 显式赋值字符串的枚举不存在反向映射
枚举合并
枚举合并:枚举可以拆分成多段,还可以与同名namespace合并。
常量枚举
常量枚举不会在值空间创建变量
所有引用常量枚举的地方都被替换为对应的值
(但是可以通过perserveConstEnum编译器选项来控制)
{
"compilerOptions" :{
"preserveConstEnum" : true
}
}
null,undefined,void,never
我们可以对比 JS、TS,来具体看看null,undefined,void,never:
JS 中:
- Undefined = {undefiend},应该表示尚未定义, 但是,实际上表示已声明,未赋值
- Null = {null},表示已声明,值为null或值为空
TS 中:
- void类型:函数没有显式返回值
- never类型:函数无法返回
- 在JS,void是个一元操作符: 它执行后面的表达值, 然后无条件返回undefined。
- 在ES1.3之前,undefined是无法直接访问的! 只能通过void(0)这种形式得到。ES1.3将 undefined添加到了global object上之后才可以访问。
总结
上述我们学完了 Typescript
的基础知识,大家应该对 ts 的基本类型有了认知,打好基础后,我们才能继续进阶学习哦~