TypeScript

洪季萌
2023-12-01

TypeScript

动态类型语言(Dynamically Typed Language):在运行期间才会做数据类型的检查,编程是不给定变量的类型,例如JavaScript、Ruby

静态类型语言(Statically Typed Language):数据类型检查发生在编译阶段,编程时需要声明数据类型,例如TypeScript、C、C++、Java

安装typescript

npm install -g typescript //安装的是最新版
npm install -g typescript@3.7.2 //安装指定版本
tsc -v //查看编译器(typescript compiler)版本

编译.ts文件为js文件,会在.ts文件目录下多一个.js文件

tsc hello.ts

上面命令每次都要转为js文件,再运行js文件,非常繁琐,安装ts-node插件,将两步操作合为一步

npm i -g ts-node

运行.ts文件

ts-node hello.ts

此时有报错:

错一:Cannot find name ‘console’. Do you need to change your target library? Try changing the ‘lib’ compiler option to include ‘dom’.

执行下面命令即可,这是安装了第三方的声明文件的库

npm install -D tslib @types/node

错二:运行ts文件时报错,Parameter ‘name’ implicitly has an ‘any’ type.

tsconfig.json

1.添加"noImplicitAny": false,即将你定义的数据类型 ,隐式具有“any”类型

2.或者 “strict”: true,改为false ,即关闭严格模式

查看目录下的文件都有什么

ls

删除.js文件

rm -rf hello.js

JS的七个基础类型(ES6后加的Symbol、BigInt)中,undefined和null是其他所有类型的子类型,例如在TS中,

let isDone: boolean = false
ler u: undefined = undefined

let num: number = undefined //这样是对的

any类型和联合类型

//any类型
let notSure: any = 1
notSure = "aaa"
notSure = true
notSure.myName
notSure.getName() //虽然以上都不报错,但是不明确的话编辑器不会提示可能会使用的方法

//联合类型
let numberOrString: number | string = 234
numberOrString = 'sss'

Array和Tuple

let arrOfNumbers: number[] = [1, 2, 3, 5] //一个数组里面元素必须是数字
arrOfNumbers.push(6) //可以
arrOfNumbers.push("8") //报错,数组的方法也会有值的限制

//如果数组中元素每项有不同的类型,用tuple,一定程度上限制了数据类型的数组
let user: [string, number] = ['li', 28] //如果后面这里少些或者多写元素也不行

interface接口

对对象的形状(shape)进行描述;

对类(class)进行抽象,在下面;

Duck Typing(鸭子类型),在下面泛型那里

interface Person {
    name: string; //注意:这里用分号分隔
    age: number;
    address?: string; //可选属性,可有可无
    readonly id: number; //只读属性,对象的字段只能在创建的时候被赋值
    //readonly和const功能类似,区别是readonly是用在属性上的,const是用在变量上的
}

//这样定义的变量如果属性多了或者少了都不被允许
let li: Person = {
    name: 'li', //这里分隔是正常用的逗号
    age: 20,
    id: 6
}

li.id = 11 //报错,因为id是只读属性

函数和类型推断(type inference)

//函数声明
function add(x: number, y: number): number { //参数是number类型,参数个数确定,返回是number类型
	return x + y
}
let result = add(2, 4)
//可选参数
function add(x: number, y: number, z?: number): number { //可选参数必须放在确定参数后面
	if(typeof z === 'number') {
		return x + y + z
	} else {
		return x + y
	}
}
//参数默认值,同时也是可选参数,实现不传的话直接用默认值
function add(x: number, y: number, z: number = 10): number {
	if(typeof z === 'number') {
		return x + y + z
	} else {
		return x + y
	}
}

//函数表达式
const add = function(x: number, y: number, z: number = 10): number {
	if(typeof z === 'number') {
		return x + y + z
	} else {
		return x + y
	}
}
//现在的add是函数类型,像下面这样写,=>不是箭头函数,是ts声明函数类型返回值的一种方法
//把鼠标放在变量名上,在没有明确指定类型的时候,ts就会自动进行类型推断
//add2是函数类型且等于add
const add2: (x: number, y: number, z?: number) => number = add

类Class

面向对象(OOP)三大特性:封装、继承、多态(由继承而产生的不同的类,对同一种方法可以有不同的响应)

class Animal {
    name: string;
    constructor(name: string) {
        this.name = name
    }
    run() {
        return `${this.name} is running`
    }
}

const snake = new Animal('lily')
// console.log(snake.run())

class Dog extends Animal {
    bark() {
        return `${this.name} is barking`
    }
}

const xiaobao = new Dog("xiaobao")
// console.log(xiaobao.run())
// console.log(xiaobao.bark())

class Cat extends Animal {
    run() {
        return 'Meow,' + super.run()
    }
}

// class Cat extends Animal {
		//这里如果写为constructor(name),
		//报错Parameter 'name' implicitly has an 'any' type.上面有记录
//     constructor(name: string) {
//         super(name)
//         console.log(this.name)
//     }
//     run() {
//         return 'Meow,' + super.run()
//     }
// }

const maomao = new Cat('maomao')
console.log(maomao.run())

修饰符(用于权限管理):public、privated、protected(子类可以访问这个属性,外部不行)、readonly(外部只可读)

static静态属性和方法

class Animal {
    protected name: string;
    static categoies: string[] = ['mammal', 'bird'] //静态属性和方法,字符串数组
    static isAnimal(a) {
    	return a instanceof Animal
    }
    constructor(name: string) {
        this.name = name
    }
    run() {
        return `${this.name} is running`
    }
}

console.log(Animal.categoies)
const snake = new Animal('lily')
console.log(Animal.isAnimal(snake))

用接口来抽象类的属性和方法

多个类的一样的方法进行描述,可以用interface接口的方式,interface之间也可以实现继承

interface Radio {
    switchRadio(triggerL: boolean): void //什么都不返回用void,这是一个新的关键字
}

interface Battery {
    checkBatteryStatus()
}

class Car implements Radio {
    switchRadio() {

    }
}

class Cellphone implements Radio, Battery {
    switchRadio() {
        
    }
    checkBatteryStatus() {
        
    }
}

//接口之间继承
interface Radio {
    switchRadio(triggerL: boolean): void
}

interface RadioWithBattery extends Radio { //接口的继承
    checkBatteryStatus()
}

class Car implements Radio {
    switchRadio() {

    }
}

class Cellphone implements RadioWithBattery {
    switchRadio() { //这个必须有,没有会报错
        
    }
    checkBatteryStatus() {

    }
}

枚举(Enum)

保存不变的值,系数依次递增,系数与值双双映射

//保存
enum Direction {
    Up,
    Down,
    Left,
    Right,
}

console.log(Direction.Up) //0
console.log(Direction.Left) //2
console.log(Direction[0]) //Up

//
enum Direction {
    Up = 10,
    Down,
    Left,
    Right,
}

console.log(Direction.Up) //10
console.log(Direction.Left) //12

//
enum Direction {
    Up = 'UP',
    Down = 'DOWN',
    Left = 'LEFT',
    Right = 'RIGHT',
}

const value = 'UP'
if(value === Direction.Up) {
    console.log('go up!')
}

常量枚举,可以提升性能(tsc xxx.ts文件后看xxx.js文件发现,少了很多代码),但是不是所有的enum都适合常量枚举,枚举值有两种:常量值和计算值,只有常量值才能进行常量枚举

const enum Direction {
    Up,
    Down,
    Left,
    Right,
}

const value = 'UP'
if(value === Direction.Up) {
    console.log('go up!')
}

泛型(Generics)

定义函数、接口或者类的时候,故意先不指定具体的类型,而是在使用的时候再指定类型

function echo<T>(arg: T): T {
    return arg
}

const result1 = echo(true)
const result2 = echo(12)

//
function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]]
}

const result3 = swap(['str', 433])

//
function echoWithArr<T>(arg: T[]): T[] {
    console.log(arg.length) //arg有length属性,不会报错
    return arg
}
const arrs = echoWithArr([1, 2, 3])

const someObj: Record<string, unknown> = { //unknown相当于any的意思,这里不能用any;never是存在但是不占位置;null是占位置的
    name1: 'li',
    name2: 'lee'
}
const someObj1: Record<string, never> = {} //就表示一个空对象,和{}不一样,{}表示这个对象可以是一切的值,never表示的是这个对象什么值都不能是

//鸭子
//上面那么写非常局限
interface IWithLength {
    length: number
}

function echoWithLength<T extends IWithLength>(arg: T): T {
    console.log(arg.length) //参数只要有length属性就行
    return arg
}

const str = echoWithLength('str')
const obj = echoWithLength({length: 10, age: 490})
const arr2 = echoWithLength([1, 2, 3])
const num1 = echoWithLength(13) //报错,没有length属性

//类,输入什么类型就能pop出什么类型
class Queue<T> {
    private data = []
    push(item: T) {
        return this.data.push(item)
    }
    pop(): T {
        return this.data.shift()
    }
}
const queue = new Queue<number>() //这里需要精确类型
queue.push(1)
console.log(queue.pop().toFixed()) //可以调用number的方法

const queue2 = new Queue<string>()
queue2.push("str")
console.log(queue2.pop().length)

//接口也可以用泛型
interface KeyPair<T, U> {
    key: T;
    value: U;
}
let kp1: KeyPair<number, string> = {
    key: 123,
    value: "str"
}
let kp2: KeyPair<string, number> = {
    key: "test",
    value: 123
}

//之前规定数组元素都是数字的方式
let arr: number[] = [1, 2, 3]
//还可以用Generics方式
let arrTwo: Array<number> = [1, 2, 3]

//之前描述函数类型的时候是用这种方式
const add = function(x: number, y: number, z: number = 10): number {
	if(typeof z === 'number') {
		return x + y + z
	} else {
		return x + y
	}
}
const add2: (x: number, y: number, z?: number) => number = add //他俩是相同的类型
//还可以用interface描述函数的类型
interface IPlus {
    (a: number, b: number) : number //注意这里没有箭头
}
function plus(a: number, b: number): number {
    return a + b
}
const a: IPlus = plus

//还可以给interface添加泛型,这样就很多变了
interface IPlus<T> {
    (a: T, b: T) : T
}
function plus(a: number, b: number): number {
    return a + b
}
function connect(a: string, b: string): string {
    return a + b
}
const a: IPlus<number> = plus
const b: IPlus<string> = connect

类型别名和类型断言

//类型别名
//像上一个代码块中对函数类型进行描述还可以用类型别名
type PlusType = (x: number, y: number) => number
function sum(x: number, y: number): number {
    return x + y
}
const sum2: PlusType = sum

//用于联合函数中,假如,输入为string,输出为string,输入为函数,输出为函数返回值
type NameResolver = () => string
type NameOrResolver = string | NameResolver
function getName(n: NameOrResolver): string {
    if(typeof n === 'string') {
        return n
    } else {
        return n()
    }
}

//类型断言
function getLength(input: string | number): number {
    //const len = input.length,如果这样会报错,因为input此时只能使用string和number共有的方法
    //所以你需要类型断言来告诉编译器你更加知道这个类型是啥,可以这样写
    //注意类型断言不是类型转换,断言为一个不存在的类型会报错
    //第一种写法,第二种更简洁
    // const str = input as String //类型断言
    // if(str.length) {
    //     return str.length
    // } else {
    //     const number = input as Number //类型断言
    //     return number.toString().length
    // }
    if((<string>input).length) { //<string>input这样写直接就是断言了
        return (<string>input).length
    } else {
        return input.toString().length
    }
}

声明文件

引入第三方库的时候需要使用它的声明文件,才能获得对应的代码补全、对应提示等功能

在.ts文件中,加入想使id为foo的元素,会直接用jQuery,

more.ts

jQuery('#foo')
//或者
$('foo')
//会报错,因为编译器并不知道$或者jQuery是什么,需要某种语法定义类型,用到declare,一般这种声明类型的语句会统一放在一个.d.ts后缀的文件中。

声明文件一般是.d.ts为后缀的,ts会解析项目中所有的.ts文件,所以其他所有.ts文件都可以声明文件中的类型定义了

例如jQuery.d.ts

declare var jQuery: (selector: string) => any
//declare可以声明变量、函数、类、接口等等

有时候发现还是没办法获得jQuery这个类型定义,需要配置一下tsc的编译器,新建tsconfig.json文件,里面写关于tsc的编译器的配置

tsconfig.json

{
	"include": ["**/*"] //编译当前文件夹下的所有文件
}

更简洁的方法

官方给了@types,统一由DefinitelyTyped组织管理,里面有各种各样的第三方的声明文件,比如jquery,需要安装@type/jquery,npm install --save @types/jquery

此时再使用jQuery(‘#foo’)就不会报错,而且还会提示用法

可以在microsoft.github.in/TypeSearch查找第三方库,当第三方库没有声明文件的话,就需要自己写声明文件了

 类似资料: