Typescript 基础知识梳理
Typescript 包含了 Javascript 的所有内容,是 Javascript 的超集。在 JS 基础上,增加了静态类型检查、接口、泛型等特性,更适合大型项目开发。
一、类型声明
使用 :
来对变量或函数形参和返回值,进行类型声明
let a: string
function fun(x: number, y: number):number { ... }
let b : 'hello' //字面量类型,让变量 b 的值只能为'hello',开发中不常用
二、类型推断
TS 会根据我们的代码,进行类型推导,例如下面代码中的变量 d,只能存储数字类型
let d = -99
d = false // ts 会对此行代码报错,不能把布尔类型分配给数值类型
三、常见类型与方法
TS 的数据类型包含 JS 的所有数据类型(字符串、数值、布尔、undefined、null、symbol、bigint、Object....),还扩展了一些数据类型(any、unknown、never、void、元组、枚举......)
TS 类型中 string 与 String 的区别:
string 类型只包括字符串的原始值类型,String 类型则还包括了使用 String 包装对象定义的字符串,number 与 boolean 同理。
1、any
任意类型,一旦使用则表示放弃了对该变量的类型检查。
any 类型的变量,可以赋值给其他任意类型的变量。
2、unknown
未知类型,可以理解为一个类型安全的 any(不能直接赋值给其他类型),适用于不确定数据类型的具体类型。
unknown 会强制开发者在使用之前进行类型检查,从而提供更强的类型安全性。
let a: unknown
a = 'hello world'
let x: string
// 类型判断
if (typeof a === 'string') {
x = a
}
// 加断言
x = a as string
x = <string>a
(a as string).toUpperCase()
3、never
never 的含义是任何值都不是,一般是 TS 主动推断出来的,也可限制函数的返回值(限制函数不能有返回值,null、undefined 也不行)
function throwError(str: string):never {
throw new Error('程序执行异常:' + str)
}
4、void
通常用于函数的返回值,含义:函数不返回任何值,调用者不应依赖其返回值进行任何操作
void 与 undefined 的区别:
使用 undefined 限制函数的返回值,调用者依然可以对返回值进行操作,而使用 void 则会进行限制(调用者不能使用返回值进行任何操作)
5、object
object 与 Object 类型限制范围太过宽泛,所以开发中不常使用(object 限制非原始值类型,Object 则除了 null 与 undefined 的所有值都在允许的范围)
在开发中我们可以使用字面量类型的方式来代替 object 与 Object
let obj: {name: string, age: number}
obj = {name: 'tom', age: 18}
对于对象属性存在缺少或有多余的情况,我们可以这样操作:
let person: {
name: string,
age?: number, // 属性可选
[key: string]: any // 索引签名
}
person = { name: 'tom', age: 18, gender: 'male', city: 'beijing'}
声明函数类型(也可用接口或自定义类型的方式):
// count 变量限制为形参为 x、y 返回值为 number 类型的函数
let count: (x: number, y: number) => number
count = function(a, b) {
// 因为上面已经进行了限制,所以这里可以省略相关代码
return a + b
}
声明数组类型:
let arr1: string[] // 限制数组元素的类型为字符串
let arr2: Array<number> // 泛型,限制数组元素的类型为数值型
6、元组(tuple)
一种特殊的数组类型,可以存储固定数量,并且每个元素的类型是已知的且可以不同,也可以使用? 设置可选
let arr1: [string, number]
let arr2: [string, boolean?] // 设置可选
let arr3: [boolean, ...string[]] // 可以有任意多个 string 类型
arr1 = ['123', 456]
arr2 = ['hello']
arr3 = [false, 'hello', 'world']
7、枚举(enum)
enum 可以定义一组命名常量,增强代码的可读性,也让代码更好维护。
// 1、数字枚举,其成员的值会自动递增,且具备反向映射的特点,默认从 0 开始
enum Direction {
Up, Down
}
function walk(str: Direction) { // 限制 str 只能为 Direction 中的值
if (str === Direction.Up) {
console.log('向上走')
} else if (str === Direction.Down) {
console.log(向下走)
}
}
walk(Direction.Right)
// 2、字符串枚举,丢失了反向映射
enum Direction {
Up = 'up', Down = 'down'
}
// 3、常量枚举,使用 const 关键字定义,在编译时会被内联,避免生成冗余代码
const enum Direction {
Up, Down
}
8、type(自定义类型)
// 1、联合类型,它表示一个值可以是几个不同类型之一
type Status = number | string
type Gender = 'male' | 'female'
// 2、交叉类型
type Area = {
width: number,
height: number
}
type Address = {
room: string
}
type House = Area & Address // 必须兼具 Area 与 Address 的属性
const house: House = {
width: 100,
height: 100,
room: '3 号楼 101'
}
9、一个特殊情况
使用类型声明限制函数返回值为 void 时,TS 并不会严格要求函数返回空,这样是为了保证类似 ES6 箭头函数的简写形式的相关代码成立。
type LogFunc = () => void // 已经限制返回值为 void
const f1: LogFunc = function() {
return '此时却可以 return 任何类型的值'
}
const f2: LogFunc = () => 666
10、属性修饰符
属性的简写形式:
// 简写前
class Person {
public name: string
public age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
// 简写后,只保留构造器
class Person {
constructor(public name: string, public age: number) {}
}
11、抽象类
抽象类是一种无法被实例化的类,专门用来定义类的结构和行为,类中可以写抽象方法,也可以写具体实现。抽象类主要用来为其派生类提供一个基础结构,要求其派生类必须实现其中的抽象方法。抽象类不能实例化,其意义是可以被继承。
abstract class Package {
constructor(public weight: number) {}
// 抽象方法,无函数体
abstract calculate():number
// 具体方法
printPackage() {
console.log(`包裹的重量为:${this.weight}kg,运费为:${this.calculate} 元`)
}
}
class StandardPackage extends Package {
constructor(
weight: number,
public unitPrice: number
){ super(weight) }
calculate():number {
return this.weight * this.unitPrice
}
}
12、interface(接口)
interface 是一种定义结构的方式,主要作用是为类、对象、函数等规定一种契约,这样可以确保代码的一致性和类型安全,interface 只能定义格式,不能包含任何实现。
① interface 定义类、对象、函数的结构
// 定义类的结构
interface PersonInterface {
name: string,
age?: number, // 可选属性
readonly sex: string, // 只读属性
speak(n: number): void
}
class Person implements PersonInterface {
constructor(public name: string, public age: number) { }
speak(n: number): void {
for (let index = 0; index < n; index++) {
console.log(this.name + ',' + this.age)
}
}
}
// 定义对象的结构
const person: PersonInterface = {
name: '张三',
sex: '男',
speak: function (n: number): void {
console.log(n)
}
}
// 定义函数的结构
interface CountInterface {
(a: number, b: number): number
}
const countFn: CountInterface = function (x, y): number {
return x + y
}
② 接口的继承
interface PersonInterface {
name: string
age: number
}
interface studentInterface extends PersonInterface {
grade: string
}
const student: studentInterface = {
name: 'John',
age: 18,
grade: '12th'
}
③ 接口的可合并性
interface PersonInterface {
name: string
age: number
}
interface PersonInterface {
grade: string
}
const person: PersonInterface = {
name: 'John',
age: 30,
grade: 'A'
}
13、一些相似概念的区别
① interface 与 type 的区别
相同点:interface 与 type 都可以定义对象的结构,两者在许多场景都是可以互换的
// 使用 interface 定义 person 对象
interface PersonInterface {
name: string
age: number
speak(): void
}
// 使用 type 定义 person 对象
type PersonType = {
name: string
age: number
speak(): void
}
不同点:
interface 更专注与定义类和对象的结构,支持继承、合并;
type 可以定义类型别名、联合类型、交叉类型,但不支持继承与自动合并
② interface 与抽象类的区别
相同点:都可以定义类的格式
不同点:
接口只能描述结构,不能有任何具体实现的代码,一个类可以实现多个接口
抽象类既可以包含抽象方法,也可以包含具体方法,一个类只能继承一个抽象类
interface FlyInterface {
fly: () => void
}
interface WalkInterface {
walk: () => void
}
class Animal implements FlyInterface, WalkInterface {...}
四、泛型
泛型允许我们在定义函数、类、接口时,使用类型参数来表示未指定的类型,这些参数在具体使用时,才被指定具体的类型。泛型能让同一段代码适用于多种类型,同时仍然保证类型的安全性。
// 泛型的示例
function logData<T>(data: T) {
console.log(data)
}
logData<string>('hello')
logData<number>(123)
// 泛型可以有多个
function logData<T, U>(data: T, msg: U): T|U {
console.log(data)
console.log(msg)
return Date.now() % 2 ? data : msg
}
logData<string, string>('hello', 'world')
logData<string, number>('hello', 2)
// 泛型接口
interface PersonInterface<T> {
name: string,
age: number,
introduce: T
}
const person: PersonInterface<string> = {
name: 'zhangsan',
age: 18,
introduce: 'hello'
}
// 泛型类
class Person<T> {
constructor(public name: T) {}
}
type JobInfo = {
title: string,
salary: number
}
let p = new Person<JobInfo>({
title: '前端开发工程师',
salary: 10000
})