TS编译环境
tsc:typeScript compile r
安转
npm install typeScript -g
查看版本
tsc --Version
编译
- 方法一
tsc 文件名
- 方法二
npm i ts-node
- 方法三:通过webpack搭建一个ts的环境
搭建环境
生成tsconfig.json
tsc --init
变量的类型推导(推断)
codeWhy: 默认情况下,如果可以推导出对应的标识符的类型时,一般情况下是不加类型注解
数据类型
JavaScript类型
数组类型
// array
// arr是数组类型,但是数组中存放的是什么类型的元素?
// 一个数组中的TypeScript开发中,最好存放的数据类型是固定的
let arr = []
arr.push('123')
// 类型注解:type annotation
const arr1:string[] = []
// arr1.push(123)
const arr2:Array<string> = [] // 不推荐(jsx中是有冲突的) <div></div> ---- <string>
// arr2.push(123)
Object类型
const obj = {
name: 'zhangsan',
height: 170
}
console.log('obj', obj.name)
const obj:object = {
name: 'zhangsan',
height: 170
}
console.log('obj', obj.name)
Null Undefined
// 所以从一开始就确定N的类型
let N: null = null
let N: string = null
N = 'abc' // 如果开启了严格模式则会报错
let N:null = null;
let n2:undefined = undefined
小知识:JS
- null:空值,曾赋过值,但是目前没有值
- undefined: 没有值,从未赋值
Symbol类型
const title1 = Symbol('title')
const title2 = Symbol('title')
const info = {
[title1]: '程序员',
[title2]: 'laoshi'
}
console.log('info', info[title1])
TypeScript类型
any类型
在某些情况下,我们确实无法确定一个变量的类型,并且可能它会发生一些变化,这个时候我们可以使用any类型(类似于Dart语言中的dynamic类型)。
- 当进行一些类型断言,因为一些类型断言不能直接进行转化,as any
- 不想给某些JavaScript添加具体的数据类型时(相当于原生的JavaScript)
any的使用在类型检测的角度来看是不安全的
- 如果对于某些情况的处理过于繁琐不希望添加规定的类型注解,或者在引入一些第三方库时,缺失了类型注解,这个时候我们可以使用any:
- 包括在Vue源码中,也会使用到any来进行某些类型的适配
unknown类型
unknown是TypeScript中比较特殊的一种类型,它用于描述不确定的变量
unknown与any的区别
unknown类型只能赋值给any和unknown类型any类型可以赋值给任意类型
function foo () {
return 'abc'
}
function bar () {
return 122
}
let flag = true
// unknown类型只能赋值给any和unknown类型
let result:unknown // 最好不要使用any
// let result:any
if (flag) {
result = foo()
} else {
result = bar()
}
let mes:string = result
console.log(result);
防止数据
类型乱用,unknown类型只能赋值给any和unknown类型
void类型
void通常用来指定一个函数是
没有返回值的,那么它的返回值就是void类型
function sum(num1: number, num2: number): void{
return undefined // (返回值类型可写可不写)
}
sum(1, 2)
一般情况下,不写void
never
表示
永远不会发生值的类型
- 比如一个函数
- 死循环或抛出异常
// never应用在什么场景
function handleMessage(message: number | string)
switch (typeof message) {
case 'string':
console.log('foo');
break;
case 'number':
console.log('bar');
break;
default:
const check: never = message
}
}
handleMessage('123')
// never应用在什么场景
// function handleMessage(message: number | string)
function handleMessage(message: number | string | boolean) {
switch (typeof message) {
case 'string':
console.log('foo');
break;
case 'number':
console.log('bar');
break;
default:
const check: never = message
}
}
handleMessage('123')
handleMessage(true)
上述表明,当类型增加,switch的分支判断也应增加,若仅增加类型,走default进行never类型赋值则出现报错
tuple类型
元组类型
const info: any[] = ['bcvCoder', 16, 1.68]
let x = info[3] // x:any
console.log(x.length)
const info: [string, number, number] = ['bcvCoder', 16, 1.68]
let name = info[0] // name:string
console.log('name', name)
let x = info[3] // x:undefined
console.log(x.length)
应用
function useState<T>(state: T): [T, (newValue:T) => void] {
let currentState = state
const changeState = (newState: T) => {
currentState = newState
}
const tuple:[T, (newState: T) => void] = [currentState, changeState]
return tuple
}
const [counter, setCounter] = useState(10)
setCounter(1000)
简写
function useState<T>(state: T): [T, (newValue:T) => void] {
let currentState = state
const changeState = (newState: T) => {
currentState = newState
}
// const tuple:[T, (newState: T) => void] = [currentState, changeState]
// return tuple
return [currentState, changeState]
}
const [counter, setCounter] = useState(10)
setCounter(1000)
函数类型
// 原函数const foo = () => {}
const foo: () => void = () => {} // 增加类型 () => void
// 函数类型不是写function,function只是一个关键字
更优雅的写法
const MyFunction = () => void
const foo: MyFunction = () => {}
函数
/**
* 返回值加类型注释: ():number
* 在开发中,通常情况下可以不写返回值的类型(不写会自动推导)
*/
function sum(num1: number, num2: number): number {
return num1 + num2
}
- 和变量的类型注解一样,通常情况下不需要返回类型注解,因为TypeScript会根据return返回值推断函数的返回类型
- 某些第三方库处理方便理解,会明确指定返回 类型,但是这个看个人喜好
匿名函数
匿名函数的参数可以
不指定类型(根据上下文环境推断出来的)
// 通常情况下,在定义一个函数时,都会给参数加上类型注解的
function foo(message:string) {
}
const names = ['abc', 'cba', 'nba']
// item根据上下文的环境推导出来的,这个时候可以不添加类型注解
// 上下文中的函数,可以不添加类型注解
names.forEach((item) => {
})
对象类型
/**
* 可以是逗号也可以是分号
* {x: number, y: number} {x: number; y: number}
*
*/
function printCoordinate(point: {x: number, y: number}) {
console.log('x:坐标', point.x);
console.log('y:坐标', point.y);
}
printCoordinate({x: 10, y: 30})
可选类型
?
z?: number ----> number | undefined
function printCoordinate(point: {x: number, y: number, z?: number}) {
console.log('x:坐标', point.x);
console.log('y:坐标', point.y);
if (point.z) {
console.log('z:坐标', point.z);
}
}
printCoordinate({x: 10, y: 30})
printCoordinate({x: 10, y: 30, z: 40})
联合类型
|
function printId (id: number | string) {
console.log('你的ID是:', id);
}
printId(10)
printId('abc')
类型别名
type
type IDType = string | number | boolean
function printId(id: IDType) {}
类型断言
有时候TypeScript无法获取具体的类型信息,这个我们需要使用类型断言(Type Assertions) as 通过 document.getElementById,TypeScript只知道该函数会返回 HTMLElement ,但并不知道它具体的类型
// <img id='imgDOM' />
const el = document.getElementById('imgDOM')
el.src = 'url地址'
// <img id='imgDOM' />
const el = document.getElementById('imgDOM') as HTMLImageElement
el.src = 'url地址'
class Person {}
class Student extends Person {
saying() {}
}
function sayHello(p: Person) {
p.saying()
}
const stu = new Student()
sayHello(stu)
class Person {}
class Student extends Person {
saying() {}
}
function sayHello(p: Person) {
(p as Student).saying()
}
const stu = new Student()
sayHello(stu)
const message = 'nihao'
const num:number = message
了解
TypeScript只允许类型断言转换为
更具体或者不太具体的类型版本,此规则可防止不可能的强制转换 更具体:具体类型;不太具体:any、unknown
const message = 'nihao'
// const num:number = (message as any) as number
const num:number = (message as unknown) as number
非特别情况,
不建议使用,可能会造成类型混乱
非空类型断言
!.
/**
*
* message? -> undefined | string
*/
function printMessage(message?: string) {
console.log('message', message.length);
}
printMessage()
确定传入的参数是有值的,可以使用非空类型断言.非空断言使用的是 ! ,表示可以确定某个标识符是有值的,跳过ts在编译阶段对它的检测;
/**
*
* message? -> undefined | string
*/
function printMessage(message?: string) {
console.log('message', message!.length);
}
printMessage()
可选链
?.
当对象的属性
不存在时,会短路,直接返回undefined,如果存在,那么才会继续执行
可选链操作是ECMAScript提出的特性,但是和TypeScript一起使用更版本
type Person = {
name: string
friend?: {
name: string
age?: number
girlfriend?: {
name: string
}
}
}
const info:Person = {
name: 'BrokerCV',
friend: {
name: 'cv'
}
}
console.log(info.friend.age);
type Person = {
name: string
friend?: {
name: string
age?: number
girlfriend?: {
name: string
}
}
}
const info:Person = {
name: 'BrokerCV',
friend: {
name: 'cv'
}
}
console.log(info.friend?.age);
console.log(info.friend?.girlfriend?.name);
??和!!的作用
??
- 它是ES11增加的新特性;
- 空值合并操作符(??)是一个逻辑操作符,当操作符的左侧是 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数
const messageCon:string | null = null
const content = messageCon ?? 'cvCoder'
console.log(content);
!!
- 将一个其他类型转换成boolean类型;
- 类似于Boolean(变量)的方式
const messageCon = 'cvCoder'
const flag = !!messageCon
console.log(flag);
字面量类型
字面量类型和值保持一致
// const str: string = 'cvCoder'
const str: string = 'cvCoder'
const strType:'cv' = 'cv'
let numType:123 = 123
numType = 23
字面量类型的意义,就是必须结合联合类型
type alignData = 'left' | 'right' | 'center'
function changeAlign(align: alignData) {
console.log('修改方向', align)
}
changeAlign('left')
字面量推理
type Method = 'GET' | 'POST'
function request(url: string, method: Method) {
}
const options = {
url: 'http://www.baidu.com',
method: 'POST'
}
request(options.url, options.method)
// ptions.method存在安全隐患
options.method = '123'
解决
- 方法一
推荐方法
type Method = 'GET' | 'POST'
function request(url: string, method: Method) {
}
type RequestType = {
url: string,
method: 'GET' | 'POST'
}
const options:RequestType = {
url: 'http://www.baidu.com',
method: 'POST'
}
request(options.url, options.method)
- 方法二
type Method = 'GET' | 'POST'
function request(url: string, method: Method) {
}
const options = {
url: 'http://www.baidu.com',
method: 'POST'
}
request(options.url, options.method as Method)
- 方法三
字面量推理:as const
type Method = 'GET' | 'POST'
function request(url: string, method: Method) {
}
const options = {
url: 'http://www.baidu.com',
method: 'POST'
} as const
request(options.url, options.method)
类型缩小
什么是类型缩小呢?
- 类型缩小的英文是 Type Narrowing;
- 我们可以通过类似于 typeof padding === "number" 的判断语句,来改变TypeScript的执行路径;
- 在给定的执行路径中,我们可以缩小比声明时更小的类型,这个过程称之为 缩小;
- 而我们编写的 typeof padding === "number 可以称之为 类型保护(type guards);
常见的类型保护有如下几种:
- typeof
- 平等缩小(比如===、!==)
- instanceof
- in
- 等等...
函数类型
// void可以改为更明确的类型,如下都是返回的都是数值类型,void可以改为number
// num1、 num2两个参数名不可以省略,如果省略,类型则为参数名,真正的类型为any
type CalcFunc = (num1: number, num2: number) => void
function calc(fn: CalcFunc) {
console.log(fn(20, 30));
}
function sum(num1: number, num2: number) {
return num1 + num2
}
function mul(num1: number, num2: number) {
return num1 * num2
}
calc(sum)
calc(mul)
参数的可选类型
可选类型必须写在必选类型的后面
// y -> undefined | number
function selectedOptions(x:number, y?:number) {
console.log(x, y);
}
selectedOptions(10, 20)
selectedOptions(10)
默认值
function foo(x: number = 20, y: number = 100) {
console.log(x, y);
}
foo(10, 30) // 10 30
foo(undefined, 12) // 20 12
顺序:必传参数 -> 有默认值的参数 -> 可选参数
this
明确的指定this的类型
type ThisType = {name: string};
function eating(this: ThisType) {
console.log(this.name, 'eating---');
}
const infoFn = {
name: 'cvCoder',
eating: eating
}
infoFn.eating()
eating.call({name: 'kobe'})
eating.apply({name: 'cvCoder'})
export{}
函数重载
类
类的定义
class Person {
name: string
age: number
eating() {
console.log(this.name + ' eating');
}
}
const p = new Person()
export {}
初始化
- 方法一
class Person {
name: string = ''
age: number = 0
eating() {
console.log(this.name + ' eating');
}
}
const p = new Person()
export {}
- 方法二
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
eating() {
console.log(this.name + ' eating');
}
}
const p = new Person('cvCoder', 18)
console.log(p.name);
console.log(p.age);
p.eating()
类的继承
使用extends关键字来实现继承,子类中使用super来访问父类
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
eating() {
console.log(this.name + ' eating');
}
}
class Student extends Person {
sno: number
constructor(name: string, age: number, sno: number) {
super(name, age) // 调用父类的构造器constructor(传递name, age)
this.sno = sno
}
// overwrite
eating() {
super.eating() // 调用父类的方法
console.log('student eating');
}
studying() {
console.log('stu');
}
}
const stu = new Student('cvCoder', 18, 12345678)
console.log(stu.name);
console.log(stu.age);
console.log(stu.sno);
stu.eating()
// const person = new Person('per', 12)
// console.log('person', person.name);
// console.log('person', person.age);
// person.eating()
export {}
类的多态
父类引用指向子类对象
class Animal {
action() {
console.log('animal action');
}
}
class Dog extends Animal {
action() {
console.log('animal running');
}
}
class Fish extends Animal {
action() {
console.log('animal swimming');
}
}
// animal: dog / fish
// 多态的目的是为了写出更加具备通用性的代码
function makeActions(animals: Animal[]) {
animals.forEach(animal => {
// console.log('animal', animal);
animal.action()
})
}
makeActions([new Dog(), new Fish])
类的成员修饰符
-
public 修饰的是在任何地方可见、公有的属性或方法,默认编写的属性就是public的;
-
private 修饰的是仅在同一类中可见、私有的属性或方法;
-
protected 修饰的是仅在类自身及子类中可见、受保护的属性或方法;
class Person {
private name: string = 'cv'
// 封装了两个方法,通过方法来访问name
getName() {
return this.name
}
setName(newName: string) {
this.name = newName
}
}
const p = new Person()
// console.log(p.name);
console.log('123', p.getName());
p.setName('cvCoder')
console.log(p.getName());
export { }
class Person {
protected name: string = ''
}
class Student extends Person {
getName() {
return this.name
}
}
const stu = new Student()
console.log(stu.getName());
// console.log(stu.name); // 无法访问
类的只读属性readonly
class Person {
// readonly name: string = '123'
readonly name: string
age?: number
readonly friend?: Person
// 1.只读属性可以在爱构造器中赋值,赋值之后就不可以修改
// 2.属性本身不能进行修改,但是如果它是对象类型,对象中的属性是可以修改的
constructor(name: string, friend?: Person) {
this.name = name
this.friend = friend
}
}
const p = new Person('cvCoder', new Person('kobe'))
console.log(p.name);
console.log(p.friend);
if (p.friend) {
p.friend.age = 30
}
console.log(p.friend);
// p.name = 'ddd' // 无法为“name”赋值,因为它是只读属性
访问器 getter/setter
class Person {
// 私有属性下划线开头(习惯、规范)
private _name: string
constructor(name: string) {
this._name = name
}
// 访问器 setter/getter
// setter
set name(newName: string) {
this._name = newName
}
get name() {
return this._name
}
}
const p = new Person('cvCoder')
p.name = 'cv'
console.log(p.name);
类的静态成员
在TypeScript中通过关键字static来定义:
class Person {
// 实例属性
name: string = ''
age: number = 12
}
const p = new Person()
p.name = '124'
class Student {
static time: string = '20:00'
static attendClass() {
console.log('去学习');
}
}
console.log(Student.time); // 静态成员,直接可以通过类来访问
Student.attendClass()
抽象类
-
继承是多态使用的前提
- 所以在定义很多通用的调用接口时,通常会让调用者传入父类,通过多态来实现更加灵活的调用方式
- 但是,父类本身可能并不需要对某些方法进行具体的实现,所以父类中定义的方法,可以定义为抽象方法
-
什么是 抽象方法?在TS中没有具体实现的方法(没有方法体),就是抽象方法
- 抽象方法,必须存在于抽象类中
- 抽象类是使用abstract声明的类
-
抽象类有如下特点
- 抽象类是不能被实例的(也就是不能通过new创建)
- 抽象方法必须被子类实现,否则该类必须是一个抽象类
function makeArea(shape: Shape) {
return shape.getArea()
}
abstract class Shape {
/**
* 1.抽象函数可以没有实现体
* 2.抽象类是不能被实例的(也就是不能通过new创建)
* */
abstract getArea():number
}
class Rectangle extends Shape {
private width: number
private height: number
constructor(width: number, height: number) {
super()
this.width = width
this.height = height
}
getArea() {
return this.width * this.height
}
}
class Cirle extends Shape {
private r: number
constructor(r: number) {
super()
this.r = r
}
getArea() {
return this.r * this.r * 3.14
}
}
const circle = new Cirle(12)
const rect = new Rectangle(12, 1)
console.log(makeArea(circle));
console.log(makeArea(rect))
类的类型
class Person {
name: string = '123'
eating() {}
}
const p = new Person()
const p1:Person = {
name: 'cvCoder',
eating() {}
}
function printPerson(p: Person) {
console.log(p.name) ;
}
printPerson(new Person())
printPerson({name: 'cv', eating(){}})
接口
声明
// 1.通过类型(type)别名来声明对象类型
type InfoType = {
name: string,
age: number
}
//
//
/**
* 2.接口interface声明对象类型
* 规范:在接口名称前加 I
* 可以定义可选类型
* 可以定义只读属性
*/
interface IInfoType1 {
readonly name: string
age: number
friend?: {
name: string
}
}
const info: IInfoType1 = {
name: "cvCoder",
age: 18,
friend: {
name: 'LSL'
}
}
索引类型
// 通过interface来定义索引类型
interface IndexLanguage {
[index: number]: string
}
const frontLanguage: IndexLanguage = {
0: 'HTML',
1: 'CSS',
2: 'JS',
3: 'TS'
}
函数类型
// type Calc = (n1: number, n2: number) => number
// 可调用接口
interface CalcFn {
(n1: number, n2: number): number
}
function calc(num1: number, num2: number, calcFn: CalcFn) {
return calcFn(num1, num2)
}
const add: CalcFn = (num1, num2) => {
return num1 + num2
}
calc(20, 30 ,add)
阅读性推荐:类型别名(type)
接口的继承
interface ISwim {
swimming: () => void
}
interface IFly{
flying: () => void
}
interface IAction extends ISwim, IFly {
}
const action: IAction = {
swimming() {},
flying() {}
}
交叉类型
多个类型结合在一起
// 组合类型:组合类型
type ConnectType = number | string
type Direction = 'left' | 'right' | 'center'
// 组合类型:交叉类型
type WType = number & string // never
interface ISwim {
swimming: () => void
}
interface IFly{
flying: () => void
}
type MyType = ISwim | IFly
type MyType1 = ISwim & IFly
const obj:MyType = {
flying() {}
}
const obj1:MyType1 = {
flying() {},
swimming() {}
}
接口的实现
interface ISwim {
swimming: () => void
}
interface IEat {
eating: () => void
}
class Animal {
}
// 继承:只能实现 单继承
// 实现:实现接口,类可以实现多个接口
class Fish extends Animal implements ISwim, IEat {
swimming() {
console.log('Fish is swimming');
}
eating() {
console.log('Fish is eating')
}
}
class Person implements ISwim {
swimming() {
}
}
// 固定了只能传Fish类
// function swimAction(swimable: Fish) {
// swimable.swimming()
// }
// swimAction(new Fish())
// swimAction({swimming: function() {}})
// 编写一些公共的API:面向接口编程
// 所有 实现 了接口的类对应的对象,都是可以传入的
function swimAction(swimable: ISwim) {
swimable.swimming()
}
swimAction(new Fish())
swimAction({swimming: function() {}})
swimAction(new Person())
interface和type的区别
-
interface和type都可以用来定义对象类型,那么在开发中定义对象类型时,到底选择哪一个呢?
- 如果是定义非对象类型,通常推荐使用type,比如Direction、Alignment、一些Function;
-
如果是定义对象类型,那么他们是有区别的:
-
interface 可以重复的对某个接口来定义属性和方法;(合并)
-
而type定义的是别名,别名是
不能重复的
-
interface IPerson {
age: number
}
interface IPerson {
name: string
}
const person: IPerson = {
age: 18,
name: 'cvCoder'
}
泛型
泛型接口的使用
interface IInfo<T1, T2>{
name: T1,
age: T2
}
const student: IInfo = {
name: 'cvCoder',
age: 18
}
这里没有推理类型
- 方法一 加上类型
interface IInfo<T1, T2>{
name: T1,
age: T2
}
const student: IInfo<string, number> = {
name: 'cvCoder',
age: 18
}
- 方法二 默认类型
interface IInfo<T1 = string, T2 = number>{
name: T1,
age: T2
}
const student: IInfo = {
name: 'cvCoder',
age: 18
}
泛型类的使用
class Point<T> {
x: T
y: T
z: T
constructor(x: T, y: T, z: T) {
this.x = x
this.y = y
this.z = z
}
}
const p1 = new Point(12, 23, 24) // 自动推导类型
const p2 = new Point<number>(12, 23, 24)
const p3: Point<string> = new Point('123', '23', '34')
/**
* Point<string>
* 不推荐,因为react jsx 中会写 <>,可能会有冲突
* 解决方法一:类型推导
* 解决方法二:在JSX以外的文件中进行类型确定
*
*/
const name1: string[] = ['a', 'b', 'c']
const name2: Array<string> = ['aa', 'bb', 'cc'] // 不推荐(因为react jsx 中会写 <>)
泛型的约束
引子
泛型T无法确定传入的数据类型一定有length这个属性
使用
extends对其进行限制
interface ILength {
length: number
}
function getLength<T extends ILength>(arg: T) {
return arg.length
}
getLength(123)
getLength(true) // 报错
命名空间namespace
export namespace time {
export function format() {
return '2022-09-09'
}
}
export namespace tool {
export function format() {
return 'cvCoder'
}
}
import {time, tool} from './07_其他知识点/tool'
console.log('=======', time.format());
console.log(tool.format());
promise
promise的类型会决定then的类型
import axios, { AxiosResponse } from 'axios'
// axios的实例对象
axios.get('http://baidu.com').then((res: AxiosResponse) => {
console.log(res)
})
学习笔记记录