Typescript基础(Typescript基础与实践)
前言
我们先回顾一波 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 的基本类型有了认知,打好基础后,我们才能继续进阶学习哦~
作者:小铭子
链接:https://juejin.cn/post/7035562231391584269