TypeScript基础

1、TypeScript介绍

1.1 TypeScript 是什么:Type + JavaScript

TypeScript(简称:TS)是 JavaScript 的超集(JS 有的 TS 都有)。
在 JS 的基础之上,为 JS 添加了 类型支持
TypeScript 是微软开发的开源编程语言,可以在任何运行 JavaScript 的地方运行

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

背景:JS的类型系统存在 先天缺陷 ,JS代码中绝大部分错误都是类型错误(Uncaught TypeError)

原因:增加了找Bug,改Bug的时间,严重影响开发效率。

TypeScript属于静态类型的编程语言,静态编程语言在编译器做类型检查。

JavaScript属于动态类型的编程语言,动态编程语言在执行器做类型检查。

代码是先编译再执行的
TS 可以 提前到在编写代码的同时 就发现代码中的错误, 减少找 Bug、改 Bug 时间
1、3 TypeScript 相比 JS 的优势
更早(写代码的同时)发现错误,减少找Bug、改Bug的时间,提高开发效率
程序中的任何位置的代码都有代码提示,增强了开发体验
支持ECMAScript语法,有强大的类型系统,会自动进行TS类型推断

2、TypeScript体验

2.1 安装编译TS的工具包:

因为node.js / 浏览器只认识JS代码,不认识TS代码,所以需要将TS代码转换为JS代码才能运行。

安装命令: npm install -g typescript

这是typescript包,用来编译ts代码,提供了tsc命令,可以将 TS 代码转化为 JS代码

2.2 编译并运行TS代码:

①:创建hello.ts文件

②:将TS编译为JS:在终端中输入命令,tsc hello.ts (此时,目录中会编译生成一个hello.js文件)

③:执行JS代码:在终端输入命令 node hello.js 回车输出内容
注意:由 TS 编译生成的 JS 文件 ,代码中就 没有类型信息 了。

2.3 简化运行TS的步骤:

①:使用 ts-node 包,直接在node.js中执行TS代码

安装命令:npm install -g ts-node (ts-node 包提供了 ts-node 命令)

使用:ts-node hello.ts 回车

解释:ts-node 命令在内部偷偷将 TS 转化为了 JS,再运行JS代码

3、TyepScript常用类型

3.1 类型注解:

let age: number = 18

// : number 就是类型注解

// 约定了什么类型,就只能给变量赋值该类型的值,否则,就会报错

3.2 常用基础类型:

分为 2 类:JS已有类型、TS新增类型

①:JS已有类型:

原始类型:number / string / boolean / null / undefined / symbol

对象类型:object (包括:数组、对象、函数)

②:TS新增类型:

联合类型、自定义类型(也叫类型别名)、接口、元组、字面量类型、枚举、void、any等

3.3 原始类型:

let age: number = 18

let myName: string = '广州南方学院'

let isLoading: boolean = false

let a: null = null

let b: undefined = undefined

let s: symbol = Symbol()

3.4 数组类型:

// 推荐使用

let numbers: number[] = [1, 3, 5, 6]

// 不推荐使用

let numbers1: Array<number> = [1, 3, 5, 6]

let b: boolean[] = [true, false]

// 联合类型 |

// 添加小括号,表示:首先是数组,然后这个数组中能够出现 number 或 string 类型的元素

let arr: (number | string)[] = [1, 3, 5, 'a', 'b']

// 不添加小括号,表示:arr1 既可以是number 类型,又可以是 string[]

let arr1: number | string[] = ['a', 'b']

let arr2: number | string[] = 123

3.5 类型别名:
使用 type 关键字来创建类型别名。
类型别名 (自定义类型):为任意类型起别名。
使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名, 简化该类型的使用 。

// 类型别名

type CustomArray = (number | string)[]

let arr: CustomArray = ['l', 5, 'y', 2, 't', 0]

let arr1: CustomArray = ['k', 5, 'e', 2, 'a', 0, 'i']

3.6 函数类型:

函数的类型实际上指的是:函数参数和返回值类型

/**

*1、单独指定参数、返回值类型

*/

// 普通函数

function add(num1: number, num2: number): number {

return num1 + num2

}

// 调用

console.log(add(1, 2)) // 3

// 箭头函数

const addNum = (num1: number, num2: number): number => {

return num1 + num2

}

// 调用

console.log(addNum(2, 4)); // 6

/**

* 2、同时指定参数、返回值的类型

*/

const sub**: (num3: number, num4: number) => number** = (num3, num4) => {

return (num3 - num4)

}

console.log(add(6, 3));

如果函数没有返回值,那么,函数返回值类型为:void

function greet(name: string): void {

console.log('Hello' + name)

}

// 调用

greet('lyt')

可选参数::在可传可不传的参数名称后面添加 ?(问号)
注意: 可选参数只能出现在参数列表的最后 ,也就是说可选参数后面不能再出现必选参数

function mySlice(start?: number, end?: number): void {

console.log('起始索引:', start, '结束索引:', end);

}

mySlice()

mySlice(1)

mySlice(2, 3)

必选参数不能位于可选参数后

// 错误演示

function mySlice(start?: number, end: number): void {

console.log('起始索引:', start, '结束索引:', end);

}

3.7 对象类型:
TS 中 对象的类型 就是在 描述对象的结构 (有什么类型的属性和方法)

/* let person: {name: string;age: number;sayHi(): void;greet(name: string): void} = {

name: 'John',

age: 18,

sayHi() { },

greet(name) { }

}

*/


let person: {

name: string

age: number

// sayHi(): void

sayHi: () => void

greet(name: string): void

} = {

name: 'John',

age: 18,

sayHi() { },

greet(name) { }

}

对象类型的可选属性

// 对象类型的可选属性

function myAxios(config: { url: string; method?: string }) {

console.log(config);

}

myAxios({

url: "http://www.baidu.com"

})

3.8 接口:直接描述对象的结构,比如:人
当一个对象类型被多次使用时,一般会使用 接口 ( interface )来描述对象的类型,达到 复用 的目的

interface Person {

name: string

age: number

sayHi(): void

}

let person: Person = {

name: 'xwj',

age: 18,

sayHi() { }

}

let person1: Person = {

name: 'lyt',

age: 18,

sayHi() { }

}
interface(接口)和 type(类型别名)的对比:

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

不同点:

接口:只能为对象指定类型

类型别名:可以为任意类型指定别名


使用 extends (继承)关键字实现了接口 Point3D 继承 Point2D

3.9 元组:元组类型可以确切的标记出有多少个元素,以及每个元素的类型
元组类型是另一种类型的数组,它 确切地知道包含多少个元素,以及特定索引对应的类型

// let position: number[] = [12,23,6,3] // 不严谨

// 使用元组

let position: [number, number] = [39, 52]

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

3.11 类型断言:有时候你比TS更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型。

使用 as 关键字实现类型断言。

3.12 字面量类型:

  1. str1 是一个变量(let),它的值可以是任意字符串,所以类型为:string。

  2. str2 是一个常量(const),它的值不能变化只能是 'Hello TS',所以,它的类型为:'Hello TS'。

注意:此处的 'Hello TS',就是一个字面量类型。也就是说某个特定的字符串也可以作为 TS 中的类型。

除字符串外,任意的 JS 字面量(比如,对象、数字等)都可以作为类型使用

let str1 = 'Hello TS'

const str2: 'Hello TS' = 'Hello TS'

let age: 18 = 18

function changeDirection(direction: 'up' | 'down' | 'left' | 'right') { }

// 调用

changeDirection('up')
字面量类型配合联合类型一起使用
参数 direction 的值只能是 up/down/left/right 中的任意一个

3.13 枚举:枚举成员是有值的,默认为:从 0 开始自增的数值。

枚举的功能类似于字面量类型+联合类型组合的功能,也可以表示一组明确的可选值。

枚举:定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个

// 枚举 表示一组明确的可选值。

enum Direction {

Up,

Down,

Left,

Right

}

// 使用枚举 定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个。

function changeDirection(direction: Direction) {

console.log(direction);

}

// 调用函数 并访问枚举里面的成员

changeDirection(Direction.Up)

解释:

  1. 使用 enum 关键字定义枚举。

  2. 约定枚举名称、枚举中的值以大写字母开头。

  3. 枚举中的多个值之间通过 ,(逗号)分隔。

  4. 定义好枚举后,直接使用枚举名称作为类型注解。

数字枚举:

// 枚举 枚举成员的值默认是从 0 开始

/* enum Direction {

Up,

Down,

Left,

Right

} */

// 成员设置初始值

// 第一种

/* enum Direction {

Up = 10,

Down,

Left,

Right

} */

// 第二种

enum Direction {

Up = 5,

Down = 2,

Left = 0,

Right = 1

}

// 类似于 JS 中的对象,直接通过点(.)语法访问枚举的成员

// 使用枚举

function changeDirection(direction: Direction) {

console.log(direction);

}

// 调用函数 并访问枚举里面的成员

changeDirection(Direction.Up)

字符串枚举:

// 字符串枚举 字符串枚举没有自增长行为,因此,字符串枚举的每个成员必须有初始值。

enum Direction {

Up = 'UP',

Down = 'DOWN',

Left = 'LEFT',

Right = 'RIGHT',

}

// 使用枚举

function changeDirection(direction: Direction) {

console.log(direction);

}

// 调用函数

changeDirection(Direction.Right)

// console.log(Direction);

3.14 any类型:

// 当值为any时,可以对该值进行任意操作,并且不会有代码提示。

let obj: any = { x: 0 }

obj.bar = 100

obj()

const n: number = obj

// 以上操作都不会有任何类型错误提示,即使可能存在错误

let a

a = 1

a = ''

a()

// 可以随意传内容

function add(num1, num2) { }

add(1, 2)

add('1', 2)

add(false, 1)

3.15 typeof:

  1. 使用 typeof 操作符来获取变量 p 的类型,结果与第一种(对象字面量形式的类型)相同。

  2. typeof 出现在类型注解的位置(参数名称的冒号后面)所处的环境就在类型上下文(区别于 JS 代码)。

  3. 注意:typeof 只能用来查询变量或属性的类型,无法查询其他形式的类型(比如,函数调用的类型)

console.log(typeof 'Hello TS'); // 打印 string

// typeof 查询变量或属性的数据类型

let p = { x: 1, y: 2 }

function formatPoint(point: typeof p) { }

// function formatPoint(point: { x: number, y: number }) { }

formatPoint({ x: 3, y: 5 })

//----

let num: typeof p.x

4、TypeScript高级类型

TS高级类型有 class、类型兼容性、交叉类型、泛型和keyof、索引签名类型和索引查询类型、映射类型

①:class类:

// class 基本使用

class Person {

age: number;

gender = '男'

// gender: string = '男'

}

const p = new Person()

p.age

p.gender

// 解释:

// 1、根据TS中的类型推论,可以知道Person类的实例对象p的类型是Person

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

构造函数:

// 构造函数不能有返回值类型

class Person {

age: number

gender: string

// 构造函数

constructor(age: number, gender: string) {

this.age = age

this.gender = gender

}

}

// 实例化

const p = new Person(18, '男')

console.log(p.age, p.gender);

实例方法:

class Point {

x = 1

y = 2

// 缩放

scale(n: number) {

this.x *= n

this.y *= n

}

}

// 实例化

const p = new Point()

p.scale(10) // 放大10倍

console.log(p.x, p.y); // 10 20

继承:

class Animal {

move() {

console.log('走几步');

}

}

// 继承之后,子类就会拥有父类所有的属性和方法

// 子类Dog 继承 父类Animal

class Dog extends Animal {

name = '二哈'

bark() {

console.log('狗叫');

}

}

// 实例化子类

const d = new Dog()

d.move()

d.bark()

console.log(d.name);

接口实现:

interface Singable {

name: string

sing(): void

}

// 接口实现

class Person implements Singable {

name = 'xwj'

sing() {

console.log('&可爱');

}

}

const p = new Person()

p.sing()

console.log(p.sing, p.name);

// 通过 implements 关键字让 class 实现接口

// Person 类实现接口 Singable 意味着,Person类中必须提供Singable接口中指定的所有的属性和方法

成员可见性:public(公有的) protected(受保护的) private(私有的)

public(公有的)

// 成员可见性:可以使用TS来控制class的方法或属性对于class外的代码是可见的

// 可见性修饰符:public(公有的) protected(受保护的) private(私有的)

// 父类

class Animal {

public move() {

console.log('走两步')

}

}

const a = new Animal()

a.move()

// 子类

class Dog extends Animal {

bark() {

console.log('狗叫');

}

}

// 实例化子类

const d = new Dog()

d.bark()

d.move()

成员可见性:public(公有的) protected(受保护的) private(私有的)

protected(受保护的)

// protected:表示受保护的,仅对其所在类和子类中(非实例对象)可见

// 父类

class Animal {

protected move() { // 不能在实例对象中访问

console.log('父类动物');

}

run() {

this.move()

console.log('走两步');

}

}

const a = new Animal()

// 子类 继承父类

class Dog extends Animal {

bark() {

this.move()

console.log('狗叫');

this.move()

}

}

// 实例化对象 不能子这里访问

const d = new Dog()

成员可见性:public(公有的) protected(受保护的) private(私有的)

private(私有的)

// private:表示私有的,只在当前类中可见,对实例对象以及子类也是不可见的

class Animal {

// 在类或者属性前面添加 private 关键字,来修饰该属性或方法

private run() {

console.log('Animal 内部辅助函数');

}

// 受保护的

protected move() {

this.run()

console.log('走两步');

}

// 公开的 public可省略

run() {

this.run()

this.move()

console.log('跑起来');

}

}

// 实例化父类

const a = new Animal()

// 子类

class Dog extends Animal {

bark() {

console.log('狗叫');

}

}

// 实例化子类

const d = new Dog()

d.bark()

d.run()

readonly 修饰符:

// readonly 表示只读,用来防止在构造函数之外对属性进行赋值

// class Person {

// 只读属性 可以用在类、接口和普通对象中

// readonly age: number = 18

// constructor(age: number) {

// this.age = age

// }

// // 错误演示

// // readonly setAge() { // 不能用来修饰方法,仅能用来修饰属性

// // // this.age = 20 // 报错

// // }

// }

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

class Person {

// 只读属性 可以用在类、接口和普通对象中

// 注意:只要是 readonly 来修饰的属性,必须手动提供明确的类名

readonly age: number = 18

constructor(age: number) {

this.age = age

}

}

// ----

// 接口

interface IPerson {

name: string

}

let obj: IPerson = {

name: 'xwj'

}

obj.name = '可爱' // 不是readonly 可以修饰属性值

// let obj: {

// readonly name: string

// }

// obj.name = '可爱' // 报错

②:类型兼容性:

// 演示类型兼容性:

// let arr = ['a','b','c']

// arr.forEach(item => {})

// arr.forEach((item,index) => {})

// arr.forEach((item,index,array) => {})

// 两种类型系统 1、 结构化类型系统 2、 标明类型系统

// TS采用的是结构化类型系统,也叫鸭子类型,类型关注的是值所具有的形状

// 也就是说,在结构类型系统中,如果两个对象具有相同的形状,则认为他们属于同一类型

// 两个类的兼容性演示

class Point {

x: number

y: number

}

class Point2D {

x: number

y: number

}

const p: Point = new Point2D()

准确:

// 注意:在结构化类型系统中,如果两个对象具有相同的形状,则认为他们属于同一类型,这种说法并不准确。

// 更准确的说法:对象对象类型来说,y的成员至少与x相同,则x兼容y(成员多的可以赋值给少的)

class Point {

x: number

y: number

}

class Point2D {

x: number

y: number

}

const p: Point = new Point2D()

class Point3D {

x: number

y: number

z: number

}

const p1: Point = new Point3D()

// 解释:1、Point3D的成员至少与Point相同,则Point兼容Point3D 2、所以,成员多的Point3D可以赋值给成员少的Point

接口:

// 除了class之外,TS中的其它类型也存在相互兼容的情况 ,包括:1、接口兼容性 2、函数兼容性

// 接口兼容性 类似于class,并且,class和interface之间也可以兼容

interface Point {

x: number

y: number

}

interface Point2D {

x: number

y: number

}

interface Point3D {

x: number

y: number

z: number

}

let p1: Point

let p2: Point2D

let p3: Point3D

// 正确演示

// p1 = p2 // 在赋值前使用了变量"p2"

// p2 = p3 // 在赋值前使用了变量"p3"

// 类和接口之间也是兼容的

class Point4D {

x: number

y: number

z: number

}

p2 = new Point4D()

函数参数个数:

// 在JS中省略用不到的函数实际上是很常见的,这样的使用方式,促成了TS中函数类型之间的兼容性

// 1、参数个:参数少的可以赋值给参数给参数多的

// 2、参数类型:相同位置的参数要相同或兼容

// 1 参数个数: 参数少的可以赋值给参数多的

type F1 = (a: number) => void

type F2 = (a: number, b: number) => void

let f1: F1

let f2: F2

// f2 = f1

// 错误演示:

// f1 = f2

函数参数类型:

// 参数类型: 相同位置的参数类型要相同(原始类型)或兼容(对象类型)

// 2 参数类型: 相同位置的参数类型要相同或兼容

// 原始类型:

// type F1 = (a: number) => void

// type F2 = (a: number) => void

// let f1: F1

// let f2: F2

// f1 = f2

// f2 = f1

// --

// 对象类型

interface Point2D {

x: number

y: number

}

interface Point3D {

x: number

y: number

z: number

}

type F2 = (p: Point2D) => void // 相当于有 2 个参数

type F3 = (p: Point3D) => void // 相当于有 3 个参数

let f2: F2

let f3: F3

// f3 = f2

// f2 = f3

返回值类型:

// 返回值类型,只需要关注返回值类型本身即可

// 原始类型

type F5 = () => string

type F6 = () => string

// 声明变量

let f5: F5

let f6: F6

// 正确演示

// f6 = f5

// f5 = f6

// 对象类型

type F7 = () => {

name: string

}

type F8 = () => {

name: string

age: number

}

let f7: F7

let f8: F8

// f7 = f8

// 错误演示

// f8 = f7

③:交叉类型:

// 交叉类型( & ):功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)

interface Person {

name: string

}

interface Contact {

phone: string

}

type PersonDetail = Person & Contact

let obj: PersonDetail = {

name: 'lyt',

phone: '123...'

}

// 解释:使用交叉类型后,新的类型 PersonDetail就同时具备了 Person 和 Contact的所有属性类型

// 就相当于:

/* type PersonDetail = {

name: string

phone: string

} */

④:泛型和 keyof:

// 使用泛型来创建一个函数

function id<Type>(value: Type): Type {

return value

}

// 调用泛型函数

// 1、以 number 类型调用泛型函数

const num = id<number>(10)

// 2、以 string 类型调用泛型函数

const str = id<string>('lyt')

// 3、以boolean 类型调用泛型函数

const bol = id<boolean>(false)

相关推荐
Мартин.6 分钟前
[Meachines] [Easy] Sea WonderCMS-XSS-RCE+System Monitor 命令注入
前端·xss
昨天;明天。今天。2 小时前
案例-表白墙简单实现
前端·javascript·css
数云界2 小时前
如何在 DAX 中计算多个周期的移动平均线
java·服务器·前端
风清扬_jd2 小时前
Chromium 如何定义一个chrome.settingsPrivate接口给前端调用c++
前端·c++·chrome
安冬的码畜日常2 小时前
【玩转 JS 函数式编程_006】2.2 小试牛刀:用函数式编程(FP)实现事件只触发一次
开发语言·前端·javascript·函数式编程·tdd·fp·jasmine
ChinaDragonDreamer2 小时前
Vite:为什么选 Vite
前端
小御姐@stella2 小时前
Vue 之组件插槽Slot用法(组件间通信一种方式)
前端·javascript·vue.js
GISer_Jing2 小时前
【React】增量传输与渲染
前端·javascript·面试
GISer_Jing2 小时前
WebGL在低配置电脑的应用
javascript
eHackyd2 小时前
前端知识汇总(持续更新)
前端