Typescript 笔记 | 小陷阱

73 阅读4分钟

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 两种常用的定义类型的方法,另外本文也总结了比较麻烦的泛型和对象的含义与定义,新手朋友可以一起看看。