Typescript 笔记 | 小陷阱
1. unknown 和 any 的区别
这两种类型都可以给变脸赋值任何类型。但是 any 可以将自己的值赋值给别的变量,unknown 则不行。unknown 本质上是一个类型安全的 any,不能直接赋值给其他变量。
let a: any
let u: unknown
let temp: number = 10
a = "string"
u = "string"
temp = a // 可以运行,并且temp变成了any
temp = u // 报错
当然,可以使用变量判断或者断言的形式来实现正确的赋值。
// 类型判断
if (typeof u === "number") {
temp = u
}
// 类型断言
temp = u as number
temp = <number>u
2.void 和 never 的区别
- void 用于函数没有返回值的时候
- never 用于永远不会有值
// 当函数没有写返回语句、或者只有return,或者return undefined都是void使用的场景。
function fun(): void {}
function fun(): void {
return
}
function fun(): void {
return undefined
}
// 函数用来报错,中间会throw error的时候,用never。因为每次调用就报错,所以程序中止完全没有返回值。
function fun(): never {
throw new Error("this is an error")
}
3.object 类型定义
一般情况下,我们想要定义的不是一个变量是否是 object,而是定义 object 里面的变量类型。因此很少使用let a:object会使用let a:{}使用{}来指定对象中可以包括哪些属性。
// 定义了一个stu变量为obj,其中有两个属性,name和age,这两个必须填写。
let stu: { name: string; age: number }
// 假如只填写其中一个属性,会报错
stu = { name: "张三" } // error
// 可以使用?设定变量可选,就可以实现上述效果
let stu: { name: string; age?: number }
有的时候希望一个 obj 中有一些属性,还可以添加一些额外的属性,可以使用[propName]来实现。
let stu: { name: stirng; [propName: string]: any }
stu = { name: "李四", age: 18, gender: "男" }
[propName: string]: any表示键是 string 类型,值是 any 类型。
在设置类型的时候,gender 这样的关键词往往只有男或者女,存成 string 会消耗数据库的空间,可以使用 enum 枚举类型来储存 gender。
enum Gender {
Male = 0,
Female = 1,
}
let stu: { name: string; gender: Gender }
// 最终后台会把Gender.Male转换成0
stu = {
name: "王五",
gender: Gender.Male,
}
4.interface 和 type 的区别
-
接口用来定义一个类结构
-
interface:接口,主要用于定义【对象类型】,对【对象】的结构做出定义。
-
type :类型别名,为类型创建一个新名称。
interface myInterface {
name: string
age: number
}
const obj: myInterface = {
name: "张三",
age: 18,
}
接口中所有的属性都不能有实际的值,他是一个纯抽象的概念。接口只定义对象的结构,不定义实际值。
interface myInter {
name: string
sayhello(): void
}
并且可以在定义类时,可以使其满足一个接口。
class myclass implements myInter {
name: string
constuctor(name: string) {
this.name = name
}
sayhello() {
console.log("hello")
}
}
type 可以定义基本类型别名
type mytype = string
// 下面两者等价
let a: mytype = "a"
let a: string = "a"
也可以使用 type 定义联合类型(表示一个变量可以是多种类型,每种类型之间使用|分割),例如
type mytype = string | number
let a: mytype = 1 // 正确
a = "a" // 正确
type mytype = {
x: string
y: string
}
interface myinter {
x: string
y: string
}
// 错误
type mytype = {
z: string
}
// 正确,myinter扩展了一个z属性,具有x y z三个属性
interface myinter {
z: string
}
实际上,通过上述的例子可以发现, interface 主要是用于定义类的结构,而 type 只是一个别名,更多是用于对普通变量的类型定义。并且 interface 具有扩展性,而 type 不行。
5.泛型
在定义函数或者类时,如果遇到类型不明确就可以使用泛型
function fn<T>(a: T): T {
return a
}
fn(a:10) // 不指定泛型
fn<string>(a:'hello') // 指定泛型
在这里 T 就是一个泛型,指 a 和返回值均是 T 类型,这个类型未知,只有在运行的时候才能确定是什么类型。
当然也可以指定多个泛型
function fn2<T, K>(a: T, b: K): T {
console.log(b)
return a
}
fn2<number,string>(a:123,b:'hello')
有时候也会想要限定泛型的范围。
interface Inter {
length: number
}
function fn3<T extends Inter>(a: T): number {
return a.length
}
fn3(a:'123')
这里有一点抽象,这里的泛型是指 T 必须是 Inter 的一个实现类,a 是 T 类型,所以 a 传进来的参数必须是一个含有 length 属性的对象。如例子中的'123'是一个字符串,其就有 length 属性。
总结
TS 是为了解决 js 是一门动态类型的语言而设计出来的语言。其核心目的就是为了定义类型,在代码维护上减轻压力,提前发现错误,其中核心的类型有很多,可能会有误区,例如本文总结的 any 和 unknown,void 和 never,还有 type 和 interface 两种常用的定义类型的方法,另外本文也总结了比较麻烦的泛型和对象的含义与定义,新手朋友可以一起看看。