TypeScript是什么

TypeScript(简称:ts) 是JavaScript的超集(js有的ts都有)。

TypeScript = Type + JavaScript (在js基础之上,为js添加了类型支持)。

TypeScript是微软开发的开源编程语言,设计目标是开发大型应用。可以在任何运行JavaScript的地方运行。

TypeScript为什么要为JS添加类型支持?

js的类型系统存在“先天缺陷”,js代码中绝大部分错误都是类型错误

优势一:类型化思维方式,使得开发更加严谨,提前发现错误,减少改 Bug 时间。

优势二:类型系统提高了代码可读性,并使维护和重构代码更加容易。

优势三:补充了接口、枚举等开发大型应用时 JS 缺失的功能。

Vue 3 源码使用 TS 重写,释放出重要信号:TS 是趋势。

安装解析 TS 的工具包

typescript:就是用来解析 TS 的工具包。提供了 tsc 命令,实现了 TS -> JS 的转化。

1
npm i -g typescript

简化执行TS的步骤

简化方式:使用 ts-node 包,“直接”在 Node.js 中执行 TS 代码。

安装命令:npm i –g ts-node

使用方式:ts-node hello.ts

类型注解

TypeScript 中的数据类型分为两大类:1 原始类型(基本数据类型) 2 对象类型(复杂数据类型)。

基本数据类型

常用的基本数据类型有 5 个:number / string / boolean / undefined / null。

新增 symbol

1
2
3
4
5
6
let age: number = 18
let food: string = '糖葫芦'
let isStudying: boolean = true
let u: undefined = undefined
let n: null = null
let s:symbol = Symbol()

复杂数据类型

数组
1
2
3
4
let names: string[] = ['迪丽热巴', '古力娜扎', '马尔扎']
let nums: number[] = [100, 200, 300]
let numebrs: Array<number> = [1,2,3]
let b: boolean[] = [true,false]
元组

元组类型是另一种类型的数组,它确切的知道包含多少个元素,以及特点索引对应的类型。

1
let position:[number, string, number] = [11, 's', 22]
函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function add(num1: number, num2: number): number {
return num1 + num2
}

const add = (num1: number, num2: number): number => {
return num1 + num2
}

const add: (num1: number, num2: number) => number = (num1,num2) => {
return num1 + num2
}

type fn = (num1: number, num2: number) => number
const add: fn = (num1,num2) => {
return num1 + num2
}
没有返回值的函数
1
2
3
4
function greet(name:string): void {
console.log('Hello',name);
}
greet('jack')
可选参数
1
2
3
function mySlice(start?: number, end?: number): void {
console.log('起始索引:', start, '结束索引:', end);
}
对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let person: {
sayHi: () => void
sing: (name: string) => void
sum: (num1: number, num2: number) => number
}

let person: {
name: string;
age: number;
sayHi(): void;
greet(name: string): void
} = {
name: 'jack',
age: 19,
sayHi() {},
greet(name) {}
}

类型别名

type

1
2
3
type CustomArray = (number | string)[]
let arr1: CustomArray = [1,'x',5]
let arr2: CustomArray = [1,'x',3]

字面量类型

使用模式:字面量类型配合联合类型一起使用

使用场景:用来表示一组明确的可选值列表

1
2
3
4
function changeDirection(direction: 'up' | 'down' | 'left' | 'right'){
console.log(direction);
}
changeDirection("down")

any类型

任意值

1
2
3
4
5
6
7
8
9
10
// 不推荐使用any类型,失去ts类型检测

// let obj: any = { x: 0 }

// obj.aaa
// obj.aaa = 10
// obj()

// 隐式any类型
// let a

联合类型

1
2
let arr: (number|string)[] = [1,2,3,'s']
let arr1: number|string[] = ['s']

交叉类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
interface Person {
name: string
}
interface Contact {
phone: string
}

type PersonDatail = Person & Contact
let obj: PersonDatail = {
name: 'jack',
phone: '133...'
}

interface A {
fn: (value: number) => string
}
interface B {
fn: (value: string) => string
}
type C = A & B

let c: C = {
fn:function(value){
console.log(value);
return value + ''
}
}

let c:C

c.fn('a') // number|string

索引签名类型

使用场景:当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性),此时,就用到索引签名类型了

使用 [key: string] 来约束该接口中允许出现的属性名称。表示只要是string类型的属性名称,都可以出现在对象中。

key只是一个占位符,可以换成任意合法的变量名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface AnyObject {
[key: string]: number
}
let obj: AnyObject = {
a: 1,
abc: 124,
abcde: 12345
}

interface MyArray<T> {
[n: number]: T
}
let arr: MyArray<number> = [1, 2, 3]


映射类型

映射类型:基于旧类型创建新类型(对象类型),减少重复、提升开发效率。

Key in PropKeys 表示 Key 可以是 PropKeys 联合类型中的任意一个,类似于 forin(letk in obj)。

keyof of Props 表示获取到对象类型Props中所有键的联合类型即,’a’| ‘b’ | ‘c’

注意:映射类型只能在类型别名中使用,不能在接口中使用

1
2
3
4
5
6
type PropsKeys = 'x' | 'y' | 'z'
type Type1 = { x:number; y: number; z: number }
type Type2 = { [Key in PropsKeys]: number }

type Props = { a: number; b: string; c: boolean }
type Type3 = { [key in keyof Props]: number }

索引查询(访问)类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type Props = { a: number; b: string; c: boolean }
type TypeA = Props['a'] // Props[a']表示查询类型 Props 中属性'a'对应的类型number。所以,TypeA的类型为number。
type MyPartial<T> = {
[P in keyof T]?: T[P] // ? 表示可选 T[P]表示获取T中每个键对应的类型。
}

type PartialProps = MyPartial<Props>

// 索引查询类型的其他使用方式:同时查询多个索引的类型
type Props = {
a: number
b: string
c: boolean
}
type TypeA = Props['a' | 'b'] // string | number。
type TypeB = Props[keyof Props] // string | number | boolean。

接口

直接在对象名称后面写类型注解的坏处:

1 代码结构不简洁

2 无法复用类型注解。

接口:为对象的类型注解命名,并为你的代码建立契约来约束对象的结构。 语法:

1
2
3
4
5
6
7
8
9
interface IUser {
name: string
age: number
}

let p1: IUser = {
name: 'jack',
age: 18
}

接口继承和复用

1
2
3
4
5
6
7
8
interface Point2D { x:number; y: number}
interface Point3D extends Point2D { z:number}

let person: Point3D = {
x:1,
y:2,
z:3
}

和type类型别名的区别

相同点:都可以给对象指定类型

不同点:

  1. interface 接口只能给对象指定类型

  2. type类型别名,可以为任意类型指定别名

类型推论

在 TS 中,某些没有明确指出类型的地方,类型推论会帮助提供类型。

换句话说:由于类型推论的存在,这些地方,类型注解可以省略不写!

发生类型推论的2种常见场景:1 声明变量并初始化时 2 决定函数返回值时。

注意:这两种情况下,类型注解可以省略不写! 推荐:能省略类型注解的地方,就省略(偷懒、充分利用TS类型推论的能力,提升开发效率)。

类型断言

当类型包含过大要缩小范围

1
2
3
4
5
6
const aLink = document.getElementById('link')
aLink.href // 取不到

const aLink = <HTMLAnchorElement>document.getElementById('link')
const aLink = document.getElementById('link') as HTMLAnchorElement
aLink.href // 取到了

枚举

枚举是TS为数不多的非JavaScript类型级扩展(不仅仅是类型)的特性之一。

因为:其他类型仅仅被当做类型,而枚举不仅用作类型,还提供值(枚举成员都是有值的)。

也就是说,其他的类型会在编译为JS代码时自动移除。但是,枚举类型会被编译为JS代码!

一般情况下,推荐使用字面量类型+联合类型组合的方式,因为相比枚举,这种方式更加直观、简洁、高效。

1
2
3
4
5
6
7
8
9
10
11
12
13
// enum Direction { Up, Down, Left, Right }
enum Direction {
Up = 'UP',
Down = 'Down',
Left = 'Left',
Right = 'Right'
}

function changeDirection(direction:Direction){
console.log(direction);
}

changeDirection(Direction.Up)

类型查询

typeof

1
2
3
4
5
let p = { x: 1, y :2 }
function formatPoint(point: typeof p){
console.log(point);
}
formatPoint({x: 1, y: 3})

class

TS中的 class不仅提供了class的语法功能,也作为一种类型存在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Person {
age: number
gender = '男'
// gender1:string = '男'

constructor(age: number, gender: string){
this.age = age
this.gender = gender
}
}
const p = new Person(1,'s')
console.log(p.age, p.gender);

class Point {
x = 1
y = 2

scale(n: number): void {
this.x *= n
this.y *= n
}
}

const p = new Point()
p.scale(10)
console.log(p.x, p.y);

继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 继承
class Animal {
move() {
console.log('走两步');
}
}

class Dog extends Animal {
name = '二哈'
bark() {
console.log('旺旺!');

}
}

const d = new Dog()
d.bark()
d.move()

实现接口

1
2
3
4
5
6
7
8
9
// 实现接口
interface Singable {
sing(): void
}
class Person implements Singable {
sing() {
console.log('你是我的小啊小苹果');
}
}

成员可见性

  1. public(公有的)

    默认的可见性,表示公有的、公开的,公有成员可以被任何地方访问

  2. protected(受保护的)

    在子类的方法内部可以通过 this 来访问父类中受保护的成员,但是,对实例不可见!

  3. private (私有的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 父类
class Animal {
public move() { // 公共的(默认的 可以省略)
console.log('走两步');
}
protected move1() { // 受保护的
console.log('走两步');
}
private move2() { // 私有的
console.log('走两步');
}
run(){
this.move()
this.move1()
this.move2()
}
}

const a = new Animal()
a.move()
a.move1()
a.move2()

// 子类
class Dog extends Animal {
name = '二哈'
bark() {
console.log('旺旺!');
this.move()
this.move1()
this.move2()
}
}

const d = new Dog()
d.move()
d.move1()
d.move2()

只读

使用 readonly **关键字修饰该属性是只读的,注意只能修饰属性不能修饰方法**。

接口或者表示的对象类型,也可以使用readonly

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 只读 必须手动提供类型
class Person {
readonly age: number = 18
constructor(age: number){
this.age = age
}
}


interface IPerson {
readonly name: string
}
let obj1: IPerson = {
name:'jack'
}
let obj2: {readonly name: string} = {
name:'jack'
}

泛型(类型变量)

保证类型安全的同时,可以让函数与多种不同的类型一起工作

创建泛型函数

语法:在函数名称的后面添加**<>(尖括号),尖括号中添加类型变量**,比如此处的Type。

类型变量 Type,是一种特殊类型的变量,它处理类型而不是值。

1
2
3
4
function id<Type>(value: Type): Type { 
console.log(value);
return value
}

调用泛型函数

1
2
3
4
const num = id<number>(10)
const num = id<string>('10')
// 简略写法
let num = id(10)

泛型约束

指定更加具体的类型

1
2
3
4
function id<Type>(value: Type[]): Type[] { 
console.log(value.length);
return value
}

添加约束

通过 extends 关键字使用接口,为泛型添加约束

该约束表示:传入的类型必须具有 length 属性

1
2
3
4
5
6
interface ILength { length: number }
function id<Type extends ILength>(value: Type): Type {
console.log(value.length);
return value
}
let num = id('s')

keyof 对象键名的联合类型

keyof 关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型

本示例中 keyof Type 实际上获取的是 person 对象所有键的联合类型,也就是: ‘name’l| ‘age’。

1
2
3
4
5
6
7
8
9
10
function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) {
console.log(obj[key]);
return obj[key]
}

let person = { name: 'jack', age: 18 }
getProp(person, 'age')
getProp([0], 0)
getProp(18, 'toFixed')
getProp('18', 0)

泛型接口

1
2
3
4
5
6
7
8
9
10
11
12
13
interface IdFunc<Type> {
id: (vale: Type) => Type
ids: () => Type[]
}

let obj: IdFunc<number> = {
id(value) {
return value
},
ids() {
return [1, 3, 5]
}
}

泛型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class GenericNumber<NumType> {
defaultValue: NumType
add: (x: NumType, y: NumType) => NumType

}
const myNum = new GenericNumber<number>()
myNum.defaultValue = 10


class GenericNumber<NumType> {
defaultValue: NumType
add: (x: NumType, y: NumType) => NumType
constructor(value: NumType){
this.defaultValue = value
}
}
const myNum = new GenericNumber(1)

泛型工具类型

Partial

创建所有属性为可选的新类型

1
2
3
4
5
interface Props {
id: string
children: number[]
}
type PartialProps = Partial<Props>

Readonly< Type >

创建所有属性为只读的新类型

1
2
3
4
5
6
7
interface Props {
id: string
children: number[]
}
type ReadonlyProps = Readonly<Props>
let props: ReadonlyProps = { id: '1', children: []}
props.id = 11

Pick<Type, keys>

从Type中选择一组属性来构造新类型

1
2
3
4
5
6
7
interface Props {
id: string
title: string
children: number[]
}

type PickProps = Pick<Props, 'id' | 'children'>

Record<key,Type>

构建一个对象类型,属性为keys 属性类型为Type

1
2
3
4
5
6
type RecordObj = Record<'a' | 'b' | 'c', string[]>
let obj: RecordObj = {
a:['1'],
b:['2'],
c:['3'],
}

类型声明文件

类型声明文件类型声明文件:用来为已存在的JS库提供类型信息。

1
2
3
4
// index.ts
import {count,add} from './utils.js'
console.log(count);
console.log(add(1,2));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// utils.js
let count = 10
let songName = '22'
let position = {
x: 0,
y: 0
}

function add(x,y){
return x + y
}

function changeDirection(direction){
return direction
}

const fomartPoint = point => {
console.log('当前坐标:',point);
}


module.exports = {
count,
songName,
position,
add,
changeDirection,
fomartPoint
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// utils.d.ts
declare let count: number
declare let songName: string
interface Point {
x: number
y: number
}
declare let position: Point

declare function add(x: number,y: number): number

declare function changeDirection(direction:'up'|'down'|'left'|'right'): void

type FomartPoint = (point: Point) => void

declare const fomartPoint: FomartPoint

export {
count,
songName,
position,
add,
changeDirection,
fomartPoint
}