typescript学习

第一章

什么是TypeScript?

TypeScript是JavaScript的超集,具有可选的类型并可以编译为纯JavaScript。从技术上讲TypeScript就是具有静态类型的 JavaScript 。那么,向JavaScript添加静态类型的原因是什么?
我想原因至少有三个:

  • 您可以避免经典的错误 ‘undefined’ is not a function.
  • 在不严重破坏代码的情况下,重构代码更容易。
  • 使大型、复杂的应用程序源码更易阅读。

实际上,一项研究表明,TypeScript可以检测到所有JavaScript错误的15%。
动态类型的自由特性经常会导致错误,这些错误不仅会降低程序员的工作效率,而且还会由于增加新代码行的成本增加而使开发陷入停顿。因此,JavaScript无法合并类型以及编译时缺乏错误检查,使它不适合作为企业和大型代码库中服务器端代码。

安装 Typescript
npm install -g typescript
使用 tsc 全局命令
// 查看 tsc 版本
tsc -v
// 编译 ts 文件
tsc fileName.ts

第二章

原始数据类型

Boolean
Null
Undefined
Number
BigInt
String
Symbol

// 布尔值 (可以用boolean(xxx)来做判断是否为真)
let isDone: boolean = false;
console.log(isDone)
if( Boolean(1)){
    console.log(1)
}else{
    console.log(0)
}

// 接下来来到 number,注意 es6 还支持2进制和8进制,让我们来感受下
let age: number = 10
let binaryNumber: number = 0b1111

// 之后是字符串,注意es6新增的模版字符串也是没有问题的
let firstName: string = 'viking'
let message: string = `Hello, ${firstName}, age is ${age}`

// 还有就是两个奇葩兄弟两,undefined 和 null
let u: undefined = undefined
let n: null = null

// 注意 undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量:
let num: number = undefined

// object
let obj: object = {
    name: 'xiaoji',
    age: '18'
}
// 空值 (void代表没有返回值,不能return值)
function Name(): void {
    console.log('我叫小鸡')
}
Name()
any 类型 (任意类型)
let notSure: any = 4
notSure = 'maybe it is a string'
notSure = true
// 在任意值上访问任何属性都是允许的:
notSure.myName
// 也允许调用任何方法:
notSure.getName()

第三章

Array 和 Tuple(元祖)
//最简单的方法是使用「类型 + 方括号」来表示数组:
let arrOfNumbers: number[] = [1, 2, 3, 4]
//数组的项中不允许出现其他的类型:
//数组的一些方法的参数也会根据数组在定义时约定的类型进行限制:
arrOfNumbers.push(3)
arrOfNumbers.push('abc')

// 元祖的表示和数组非常类似,只不过它将类型写在了里面 这就对每一项起到了限定的作用
let user: [string, number] = ['viking', 20]
//但是当我们写少一项 就会报错 同样写多一项也会有问题
user = ['molly', 20, true]

let NumArr: number[] = [1,2,3,4];   // 数组里必须为number
let NumStr: string[] = ['1','2','3','4'];   // 数组里必须为字符串
let Arr: any[] = [1,'A',{name:1}]   // 这里是任意类型
let typeArr: [string | number] = [1]  //  数组里只能有一个元素,且类型为string 或 number
let agmArr: [string,number] = ['1',1]  //  根据下标来对应值  (这里注意:多出下标会报错,但是可以用方法push,其中一种是OK的)
// 数组泛型 (泛型是可以自定义的,<T>)   // 满足其中一项即可
let ArrGen: Array<number | String | object> = [{name:'1'},{name:2},2,'3',{b:'bb'}]

第四章

interface (接口)
// 我们定义了一个接口 Person ( 定义接口首字母大写I 是主要区分对否为接口定义)
interface Iperson {
  name: string;	 	// name 属性只能是字符串
  age: number;		// age 属性只能是number
}

//有时我们希望不要完全匹配一个形状,那么可以用可选属性:
interface Iperson {
    age?: number; // 这个属性可有可无
}

// 有时候我们希望自定义属性 ([] 可以自定义属性,但是类型必须是所有子属性的集合)
 interface Iperson {
      [key: string]: string;
}

//接下来还有只读属性,有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性

interface Iperson {
  readonly id: number;  // 只能读取不能修改
}
viking.id = 9527  // 这里会报错

第五章

函数(到了咱们最长用的函数)
// (参数约束,和输出约束)
function add(x: number, y: number): number {
  return x + y
}
// interface 描述函数
// 定义一个接口
interface ISum {
  (x: number, y: number): number
}
// 定义一个函数
const sum = (x: number, y: number) => {
  return x + y
}
// 用ISum 接口验证 sum 函数
const sum2: ISum = sum
// 设置默认参数值
function buildName(firstName: string, lastName: string = 'Cat') {
	// 这里判断lastName参数是否存在
    if (lastName) {
        return firstName + ' ' + lastName;
    } else {
        return firstName;
    }
}
let tomcat = buildName('Tom', 'Cat');  // 有值显示值
let tom = buildName('Tom'); // 没有值则显示默认值

// 这里我们实现一个给数组添加元素的方法
function push(array, ...items) {
	// items 是除了第一个参数后所有的参数集合类型为数组
    items.forEach(function(item) {
        array.push(item);
    });
    return array
}
// 类型定义为任何类型
let arr: any[] = [];
// 调用我们写好的方法
push(arr, '1','2', '3');
console.log(arr)

第六章

类型推论,联合类型 和 类型断言
 /**
  * ==========
  * 联合类型
  * ==========
  */
// 我们只需要用中竖线来分割两个
let numberOrString: number | string 

// 当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法:
numberOrString.length	// 这里会报错因为length 不是number 和 string 的共有属性
numberOrString.toString()	// 这个是共有属性,所以不会报错

 /**
  * ==========
  * 类型断言
  * ==========
  */
// 这里我们可以用 as 关键字,告诉typescript 编译器,你没法判断我的代码,但是我本人很清楚,这里我就把它看作是一个 string,你可以给他用 string 的方法。
function getLength(input: string | number): number {
  const str = input as string
  if (str.length) {
    return str.length
  } else {
    const number = input as number
    return number.toString().length
  }
}

 /**
  * ==========
  * 类型守卫
  * ==========
  */
// 和断言思路差不多,只是拿typeof作为判断 typescript 在不同的条件分支里面,智能的缩小了范围,这样我们代码出错的几率就大大的降低了。
function getLength2(input: string | number): number {
  if (typeof input === 'string') {
    return input.length
  } else {
    return input.toString().length
  }
}

第七章

class 类(构造函数)
class Animal {
  name: string;
  // constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
  constructor(name: string) {
    this.name = name
  }
  run() {
    return `${this.name} is running`
  }
}
const snake = new Animal('lily')

// 继承的特性
class Dog extends Animal {
  bark() {
    return `${this.name} is barking`
  }
}

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

// 这里我们重写构造函数,注意在子类的构造函数中,必须使用 super 调用父类的方法,要不就会报错。
// extends  Class的继承	
class Cat extends Animal {
  constructor(name) {
    super(name)  // 调用父类的constructor(x, y)
    console.log(this.name)
  }
  arr = [1,2,3]   // 静态资源s
  run() {
    return 'Meow, ' + super.run()
  }
}
const maomao = new Cat('maomao')
console.log(maomao.run())


// class  类型检测都在下面 我就不举例说明了,根据上面的class 去做检查
// 在方法前添加该修饰符
// public   (公用,可以暴露给外面用)属性前或方法前添加改字段   
// private  (私有的,不可以暴露给外面)
// protected    (受保护的)   这个只有自己的子女(方法)后才可以使用或访问
// readonly   (只能读不能修改)

第八章

枚举
// 枚举
 enum direction{
     top,
     righ,
     bottom,
     left
 }
 // 如果不赋值,默认返回下标(可以通过下标获取或者直接.属性获取)
 console.log(direction.top)  // 0
 console.log(direction[0])  // 'top'
 

enum direction2{
     top = 10,
     righ,
     bottom,
     left
 }
 // 如果给第一个赋值任何下标如:10 ,那么它一下的都会递增+1
 console.log(direction2.righ)  // 11
 console.log(direction[12])  // 'bottom'

// 这里我们模拟后台返回的值,去做判断
// 后台返回的值
 let adminVal = 'down'
 enum direction3{
     top = 'up',
     righ = 'right',
     bottom = 'down',
     left = 'left'
 }

function keys (data) {
	let val
	for (const item in data) {
	     console.log(item)
	     if(data[item] == adminVal){
	        val= item
	     }  
 	}
 	return val
}
 
// 调用keys 方法
keys(direction3)

第9章

泛型
// 泛型解决了什么问题?,当类型不确定的时候,由外部解决! 或者 用联合类型去约束,

// 当我们的需求是传入什么,返回什么,并检查类型
function echo(arg) {
    return arg
  }
const result = echo(123)// 这时候我们发现了一个问题,我们传入了数字,但是返回了 any 并不是我们想要的
  
// 使用泛型解决此问题
function echo1<T>(arg: T): T {
    return arg
}
const result1 = echo1(123)   // 根据类型推论 检查类型
// const result2: string = echo1(123)  // 这里就会报错,返回的是number类型,不能赋值给string类型
  
// 泛型也可以传入多个值 也可以使用联合
function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]]
}
const result3  = swap(['string', 123])


// 上例中,泛型 T 不一定包含属性 length,我们可以给他传入任意类型,当然有些不包括 length 属性,那样就会报错 
// [] {},number 等去约束 一个泛型只能传入的类型
function echoWithArr<T>(arg: T[]): T[] {
    console.log(arg.length)
    return arg
  }
  echoWithArr([1,2,3])
  //echoWithArr('str') // 我们会发现,这样只能输入数组参数  不符合我们的预期

// 接口约束  <T extends 接口 >
interface IWithLength {
    length: number;  // 返回的类型必须为number
}
// 注意 extends 关键字 来使用接口
  function echoWithLength<T extends IWithLength>(arg: T): T {
    console.log(arg.length)
    return arg
  }
  
  echoWithLength('str')     // 这个是可以使用的 因为string 有length 属性
  const result4 = echoWithLength({length: 10})  // 我们发现这个也是可以使用的,只要有length 且 类型必须为number  关键字就可以使用
  const result5 = echoWithLength([1, 2, 3]) // 这个是可以使用的 因为数组 有length 属性
 
  
  // 定义一个队列构造函数
  class Queue {
    private data = [];
    push(item) {
      return this.data.push(item)
    }
    pop() {
      return this.data.shift()
    }
  }
  
  const queue = new Queue()
  queue.push(1)
  queue.push('str')
  console.log(queue.pop().toFixed())
  console.log(queue.pop().toFixed())    // 这里输出的时候会报错 (因为string类型没有toFixed方法)
  
  //在上述代码中存在一个问题,它允许你向队列中添加任何类型的数据,
  // 当然,当数据被弹出队列时,也可以是任意类型。在上面的示例中,看起来人们可以向队列中添加string 类型的数据,但是那么在使用的过程中,就会出现我们无法捕捉到的错误,
  
  // 我们使用 泛型来封装一个队列,在创建实例的时候传入想要的类型
  class Queue1<T> {
    private data = [];
    push(item: T) {
      return this.data.push(item)
    }
    pop(): T {
      return this.data.shift()
    }
  }
  const queue1 = new Queue1<number>()   // 决定他想要的类型  这样就完美的解决了以上问题

  
  //泛型和 interface 
  interface KeyPair<T, U> {
    key: T;
    value: U;
  }
  
  let kp1: KeyPair<number, string> = { key: 1, value: "str"}
  let kp2: KeyPair<string, number> = { key: "str", value: 123}

第10章

类型别名
  // 类型别名 (type 关键字)
  // 假如有多个方法多次用到同一个类型检测,那么就可以申明一个类名(相当于javascript 常量)
  type fn = (x:number,y:number) => number
  let fn1 = fn(1,2)
  let fn2 = fn('a','b')		// 这里会报错

  // 字符串字面量类型
  // 什么是字面量呢?就是定义好的常量类型必须和值全等不可修改
  const str: 'name' = 'name'
  // const str1: 'name1' = 'name2'  // 因为不全等所以会报错

  // 用别名定义了字符串,字面量类型
  type colors = 'green' | 'red' | 'yellow' | 'black'
  // 使用字面量类型
  let bg: colors = 'black'

  // 我们不难发现,别名和接口  形式好像差不多(其实它们是有区别的,下章补)
  // 那么我们什么时候用别名,什么时候用接口呢?
  // 组合形式的,采用别名(可以理解为快捷方式)
  // 独特的的类型,采用interface 

第11章

交叉类型
  // 交叉类型 (也称为 & )
  interface Iobj {
    name: string,
  }

  // 用别名和交叉类型组合成新的类型
  type Newobj = Iobj & {age: number}
  // 使用新的别名
  let Aobj: Newobj = {name: 'xiaoj',age:18}

第10章

内置类型
const a: Array<number> = [1,2,3]
// 大家可以看到这个类型,不同的文件中有多处定义,但是它们都是 内部定义的一部分,然后根据不同的版本或者功能合并在了一起,一个interface 或者 类多次定义会合并在一起。这些文件一般都是以 lib 开头,以 d.ts 结尾,告诉大家,我是一个内置对象类型欧
const date: Date = new Date()
const reg = /abc/
// 我们还可以使用一些 build in object,内置对象,比如 Math 与其他全局对象不同的是,Math 不是一个构造器。Math 的所有属性与方法都是静态的。

Math.pow(2,2)

// DOM 和 BOM 标准对象
// document 对象,返回的是一个 HTMLElement
let body: HTMLElement = document.body
// document 上面的query 方法,返回的是一个 nodeList 类型
let allLis = document.querySelectorAll('li')

//当然添加事件也是很重要的一部分,document 上面有 addEventListener 方法,注意这个回调函数,因为类型推断,这里面的 e 事件对象也自动获得了类型,这里是个 mouseEvent 类型,因为点击是一个鼠标事件,现在我们可以方便的使用 e 上面的方法和属性。
document.addEventListener('click', (e) => {
  e.preventDefault()
})

//Typescript 还提供了一些功能性,帮助性的类型,这些类型,大家在 js 的世界是看不到的,这些类型叫做 utility types,提供一些简洁明快而且非常方便的功能。

// partial,它可以把传入的类型都变成可选
interface IPerson {
  name: string
  age: number
}

let viking: IPerson = { name: 'viking', age: 20 }
type IPartial = Partial<IPerson>
let viking2: IPartial = { }

// Omit,它返回的类型可以忽略传入类型的某个属性

type IOmit = Omit<IPerson, 'name'>
let viking3: IOmit = { age: 20 }

文档

https://www.typescriptlang.org/docs/handbook/tsconfig-json.html		// 官方文档
https://www.tslang.cn/docs/handbook/typescript-in-5-minutes.html	// 中文文档
https://ts.xcatliu.com/basics/primitive-data-types.html		// 阮一峰教程
https://www.runoob.com/typescript/ts-tutorial.html		// 菜鸟教程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值