动态类型语言(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查找第三方库,当第三方库没有声明文件的话,就需要自己写声明文件了