typescript1.png
javascript 动态弱类型语言
TypeScript的纯在,静态强类型语言诞生 类型思维

优缺点

优势:

  1. 提升代码质量
  2. 增强代码可维护性
  3. 提升开发效率
  4. 重塑类型思维

成本.

  1. 思维转化
  2. 对接现有开发生态
  3. 项目迁移
  4. 接口,声明文件的维护
  5. ….

静态类型语言:在编译阶段确定所有变量的类型
动态类型语言:在执行阶段确认所有变量的类型

强类型语言:强制数据类型定义的语言,
也就说一旦定义了变量为某个数据类型,如果不通过强制类型转换,将无法更改类型
弱类型语言:数据类型可以被忽略的语言,
跟强类型相反
typescript2.png
TS会在编码阶段进行类型检查
特性:
类型检查,
语言扩展,
工具属性:编译成javascript
typescript3.png

基础篇

环境搭配

1.npm init -y 初始化项目生成package.json
1.1 淘宝镜像 npm config set registry https://registry.npm.taobao.org
查看配置 npm config get registry
查看proxy npm config get proxy 设置 npm config set proxy null
2.npm install typescript -g 全局安装typescript
3.tsc —init 初始化typescript 创建tsconfig.json
4. 编译ts文件 tsc ./index.ts
5. npm i webpack webpack-cli webpack-dev-server -D 安装webpack相关
npm i html-webpack-plugin -D 插件
npm i html-webpack-plugin -D 插件
npm install ts-loader typescript -D
…一堆插件
6. 生产,开发配置,package.json — script

  1. "start": "webpack-dev-server --mode=development --config ./build/webpack.config.js",
  2. "build": "wbepack --mode=production --config ./build/webpack.config.js",

类型注解 :相当于强类型语言中的类型声明

语法:(变量/函数) :type 联合语法

// todo
元祖 数量和类型已知的数组

普通枚举
枚举
image.png

// 常量枚举
image.png

any 如果变量为any 就跟js差不多, 不进行类型检查

null

let element: (HTMLElement | null) = document.getElementById(‘root’);
//非空断言 常用
element!.style.color = ‘green’;

// structNullChecks 则不能把null undefined 赋值为xxx

image.png
image.png

类型推导

  1. //基本类型
  2. let bool: boolean = true
  3. let number: number = 1212
  4. let str: string = '112121'
  5. //引用类型-数组
  6. let arr: number[] = [1, 2, 3]
  7. //泛型
  8. let arr2: Array<number> = [1, 2, 3]
  9. //联合类型
  10. let arr3: Array<number | string> = ['111', 222]
  11. //元组 数量和类型已知的数组
  12. let tuple: [number, string] = [1, 'string']
  13. //元组越界 可以添加,但是不能访问
  14. tuple.push('newString')
  15. // console.log(tuple)
  16. for (let A = 0; A < tuple.length; A++) {
  17. console.log(tuple[A])
  18. }
  19. //函数 形参必须定义类型
  20. let add = (x: number) => x
  21. //对象
  22. let obj: { x: number, y: number } = {
  23. x: 11,
  24. y: 22
  25. }
  26. //如果之定义了对象为object类型.不允许修改
  27. obj.x = 333
  28. // symbol
  29. let s1: symbol = Symbol()
  30. let s2: symbol = Symbol()
  31. console.log(s1===s2) //false
  32. //any类型 与js没有区别,
  33. let x
  34. x= 1
  35. x='222'
  36. x=[]
  37. //never类型
  38. let error =()=>{
  39. throw new Error('')
  40. }
  41. let endless= ()=>{
  42. while(true){}
  43. }

枚举类型 :一组有名字的常量集合

ts1.png
1.枚举的成员都是只读,不允许修改
2.不同枚举成员是不允许比较
3.开发中可以把常量抽取出来用作枚举形式
数组枚举 ,字符串枚举, 异构枚举, 枚举成员 ,常量枚举

  1. //数组枚举 反向映射
  2. enum Num {
  3. one = 3, //3 默认从0开始 如果设置了值,会递增
  4. two,//3
  5. three = 10,//10
  6. four//11
  7. }
  8. console.log(Num)
  9. //字符串枚举 枚举定义第一个字符要大写
  10. enum Str {
  11. success = '成功',
  12. fail = '失败'
  13. }
  14. //异构枚举
  15. enum Answer {
  16. n,
  17. y='Yes',
  18. b= 'Bool',
  19. }
  20. //枚举成员 编译的时候已经知道结果
  21. enum Char {
  22. a,//没有定义值,默认数字0
  23. b=Char.a,//枚举的引用
  24. c=1+2,//表达式定义值
  25. //computed计算成员,运行时才执行
  26. d=Math.random(),
  27. e ='123'.length
  28. }
  29. //常量枚举 编译阶段会被移除 ,不需要对象,只需要对象的值
  30. const enum Month {
  31. Jan,
  32. Feb
  33. }
  34. let month = [Month.Jan,Month.Feb] //编译阶段不会又常量枚举

接口

理解:接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。

  1. //定义接口里面的值
  2. interface List {
  3. readonly id: number //readonly 代表只读属性
  4. name: string
  5. age?: number //?代表可选属性
  6. }
  7. //引用接口
  8. interface Result {
  9. data: List[]
  10. }
  11. //传入参数是一个接口对象
  12. function render(result: Result) {
  13. result.data.forEach(item => {
  14. console.log(item.id, item.name)
  15. if (item.age) console.log(item.age)
  16. })
  17. }
  18. let result = {
  19. data: [
  20. {id:1,name:'小明',age:20},
  21. {id:2,name:'小红'},
  22. ]
  23. }
  24. render(result)
  25. //定义不确定的接口,全部都是字符串
  26. interface StringArray {
  27. [index:number]:string
  28. }
  29. let charts:StringArray = ['A','B','C']
  30. interface Names {
  31. [x:string]:string
  32. [z:number]:any
  33. }

异构接口

  1. //别名定义函数 接口定义
  2. type Add = (x: number, y: number) => number
  3. //实例定义
  4. let addFun: Add = (a, b) => a + b
  5. //调用
  6. console.log(addFun(1, 2))
  7. //混合类型的接口
  8. interface Lib{
  9. ():void
  10. version:string,
  11. doSomething():void
  12. }
  13. function getLib(version:string){
  14. let lib :Lib =(()=>{}) as Lib //断言
  15. lib.version =version
  16. lib.doSomething = () => {
  17. console.log(`版本号:${lib.version}`)
  18. }
  19. return lib
  20. }
  21. let lib1 =getLib('1.0')
  22. let lib2 =getLib('2.0')
  23. lib2.doSomething()

函数定义汇总 自变量定义 别名定义 接口定义 剩余参数 函数重载

1.函数重载,编译的时候会创建函数重载列表

  1. //函数定义
  2. function add1(x: number, y: number): number {
  3. return x + y
  4. }
  5. //自变量定义
  6. let add2: (x: number, y: number) => number
  7. //别名定义
  8. type add3 = (x: number, y: number) => number
  9. //接口定义
  10. interface add4 {
  11. (x: number, y: number): number
  12. }
  13. //调用函数参数个数必须相同,
  14. // 可在参数中添加?设置为可选参数,可选参数必须在必选参数在后
  15. function add5(x: number, y = 2, z: number, q = 1) {
  16. return x + y + z + q
  17. }
  18. console.log(add5(1, undefined, 3))
  19. //剩余参数
  20. function add6(x: number, ...args: number[]) {
  21. return x + args.reduce((prev, cur) => prev + cur)
  22. }
  23. console.log(add6(1, 2, 3, 4, 5, 6)) //21
  24. //函数重载 利用参数类型实现重载
  25. function heavyLoad(...args: number[]): number
  26. function heavyLoad(...args: string[]): string
  27. function heavyLoad(...args: any[]): any {
  28. let first = args[0]
  29. if (typeof first === 'number') {
  30. return args.reduce((prev, cur) => prev + cur)
  31. } else if (typeof first === "string") {
  32. return args.join("_")
  33. }
  34. }
  35. console.log(heavyLoad(1, 2, 3, 4, 5))
  36. console.log(heavyLoad('1', '2', '3'))

类 class 会覆盖es6的类 创建,class 继承 extends

  1. class Dog {
  2. constructor(name: string) {
  3. this.name = name
  4. }
  5. name: string
  6. sayHi() {
  7. console.log(`我叫${this.name}`)
  8. }
  9. run() {
  10. }
  11. }
  12. //类的成员在实例上面,不在原型上
  13. let dog = new Dog("旺财")
  14. dog.sayHi()
  15. //继承
  16. class Husky extends Dog {
  17. constructor(name: string, age: number) {
  18. super(name)
  19. this.age = age
  20. }
  21. age: number
  22. play() {
  23. console.log(`我叫${this.name},我会卖萌玩耍,我有年龄`)
  24. }
  25. sayHi1 = () => {
  26. console.log('我有年龄' + this.age)
  27. }
  28. }
  29. let husky = new Husky('哈稀奇', 14)
  30. husky.sayHi1()
  31. husky.sayHi()
  32. husky.play()

修饰符

1.public
2.private
3.protected
4.readonly
5.static

抽象类, 只能被继承,不能被实例 多态

  1. // 创建抽象类 相当于把公共的东西抽到同一个抽象类里面
  2. abstract class Animal {
  3. eat() {
  4. console.log('eat')
  5. }
  6. // 抽象方法 继承抽象类一定要有实现
  7. abstract sleep():void
  8. }
  9. class Dog1 extends Animal {
  10. constructor(name: string) {
  11. super()
  12. this.name = name
  13. }
  14. name: string
  15. sleep(): number {
  16. console.log('Dog_sleep')
  17. return 1
  18. }
  19. }
  20. let dog1 =new Dog1("哈士奇")
  21. dog1.eat()
  22. dog1.sleep()
  23. //多态实现
  24. class Cat extends Animal{
  25. sleep(): void {
  26. console.log("cat_sleep")
  27. }
  28. }
  29. let cat = new Cat()
  30. let animals:Animal[]= [dog1,cat]
  31. animals.forEach(item=>{
  32. item.sleep()
  33. })
  34. //this.链式操作
  35. class WorkFlow{
  36. step1(){
  37. return this
  38. }
  39. step2(){
  40. return this
  41. }
  42. }
  43. new WorkFlow().step1().step2()
  44. //父类与子类都能链式操作
  45. class MyFlow extends WorkFlow{
  46. next(){
  47. return this
  48. }
  49. }
  50. new MyFlow().step2().next().next().step1()

类与接口之间的关系

  1. 类实现接口必须声明接口的所有成员
  2. 接口只能约束类的共有成员
  3. 接口之间能相互继承,多继承(,分割)
  4. 接口继承类

ts2.png

  1. interface Human {
  2. name: string
  3. eat(): void
  4. }
  5. //类继承接口,需要把所有接口成员实现
  6. class Asian implements Human {
  7. constructor(name: string) {
  8. this.name = name
  9. }
  10. name: string
  11. eat(): void {
  12. console.log("Eat_Fun")
  13. }
  14. sleep(): void {
  15. console.log("sleep_fun")
  16. }
  17. }
  18. interface Man extends Human {
  19. run(): void
  20. }
  21. interface Child {
  22. cry(): void
  23. }
  24. // 接口之间的继承关系
  25. interface Boy extends Man, Child {
  26. age: number
  27. }
  28. let boy: Boy = {
  29. name: "杨明",
  30. age: 27,
  31. run() {},
  32. eat() {},
  33. cry() {
  34. console.log(this.name + "crying")
  35. return this
  36. }
  37. }
  38. boy.cry()
  39. //接口继承类
  40. class Auto {
  41. state:number =1
  42. }
  43. interface AutoInterface extends Auto{
  44. //接口里面的成员
  45. interfaceState: number
  46. }
  47. class C implements AutoInterface{
  48. constructor(name:string,state:number) {
  49. this.name = name
  50. this.state =state
  51. }
  52. name:string
  53. state:number
  54. interfaceState =2
  55. }
  56. //实例
  57. let c = new C("小明",1)
  58. class Bus extends Auto implements AutoInterface{
  59. constructor(interfaceState:number) {
  60. super();
  61. this.interfaceState =interfaceState
  62. }
  63. interfaceState:number
  64. }
  65. let bus = new Bus(1)
  66. console.log(bus)

泛型(重要概念) 函数或者类支持多种数据类型

泛型理解:不需要预先确定的数据类型,具体的类型在使用的时候才确定

  1. //泛型函数的定义 ,定义时不知道参数类型,返回值类型 但是两个类型必须一致
  2. function log<Type>(value: Type): Type {
  3. console.log(value)
  4. return value
  5. }
  6. //1.调用
  7. log<string>("小明") //调用前定义类型
  8. log("小明")
  9. //2.别名
  10. type Log =<Type>(value:Type) => Type
  11. let myLog:Log = log
  12. myLog("小明")
  13. //************************************************************************
  14. //泛型接口
  15. interface Log<Type = string> {
  16. (value: Type): Type
  17. }
  18. let mylog :Log<number> = log
  19. mylog(1)

泛型的好处:

  1. 函数和类可以轻松地支持多种类型,增强程序的扩展性
  2. 不必写多条函数重载,冗长的联合类型声明,增强代码可读性
  3. 灵活控制类型之间的约束

    泛型的约束

    1. // 泛型函数
    2. class Log <Type> {
    3. run(value:Type) :Type{
    4. console.log(value)
    5. return value
    6. }
    7. }
    8. let log1 = new Log<number>()
    9. log1.run(1111)
    10. let log2 =new Log()
    11. log2.run({a:11})
    12. //***************************************
    13. // 泛型的约束
    14. interface Length {
    15. length:number
    16. }
    17. //传入泛型参数必须有length属性
    18. function log<Type extends Length> (value:Type) :Type {
    19. console.log(value, value.length)
    20. return value
    21. }
    22. log([])
    23. log("123")

    类型检查机制

    理解:TypeScript编译器在做类型检查时,所秉承的一些原则,以及表现出的一些行为
    作用:辅助开发,提高开发效率

  4. 类型推断

  5. 类型兼容性
  6. 类型保护

    类型推断

    理解:不需要制定变量的类型(函数的返回值类型),TypeScript可以根据某些规则自动地为其推断出一个类型
    1.基础类型推断
    2.最佳通用类型推断
    3.上下文类型推断
    1. //基础类型推断 从右到左推断
    2. let any //any类型
    3. let a = 1 //number
    4. let b = "1231231" //string
    5. let c1 = (x = 1) => x + 1 //number :number
    6. //最佳通用类型推断
    7. let arr1 = [1, null, "123"] //(number | null | string)[]
    8. //上下文类型推断
    9. window.onkeydown = (event) => {
    10. //console.log(event.buttons)
    11. }

    类型兼容性

    理解:当一个类型Y可以被赋值给另外一个类型X时,我们就可以说类型X兼容类型Y
    X兼容Y X(目标类型) = Y(源类型)
    源类型必须具备目标类型的属性 我的理解:如果右边包含左边,那么左边兼容右边
    只有兼容类型才能被赋值
    老师口诀
    结构之间兼容:成员少的兼容成员多的
    函数之间兼容:参数多的兼容参数少的 ```typescript let a1 :string =”aaa” a1 =null //关掉null类型检查的情况,a1兼容null

//接口兼容性 interface X { a:number b:number } interface Y { a:number b:number c:number d:number } let x1 :X ={ a:1, b:2 } let y1 :Y ={ a:10,b:11,c:12,d:13} x1 =y1 //右边包含左边,那么左边兼容右边,可赋值 console.log(x1) //{ a:10,b:11,c:12,d:13}

//函数兼容的3个条件 //1.参数个数 //2.参数类型 //3.返回值类型

//枚举类型的兼容性 enum Fruit { Apply, Banana } enum Color { Red ,Yellow } let fruit :Fruit.Apply = 3 //枚举类型兼容其他类型 let no:number = Fruit.Apply //枚举之间不兼容 //Type ‘Fruit.Apply’ is not assignable to type ‘Color.Red’. // let color:Color.Red = Fruit.Apply

//类的兼容性 //1.静态成员与构造函数是不参与比较 //2.如果有私有成员,就不兼容 //3.如果子类的继承父类,子类的实例与父类的实例是兼容的 class A { constructor(p:number) {} id:number =1 private name :string = “” } class B{ static c :number = 1 constructor(p:number ,b:number ) {} id:number = 2 private name :string = “” } let aa = new A(1) let bb = new B(1,1) class CC extends A{ age:number =27 } let cc =new CC(1) aa =cc //Property ‘age’ is missing in type ‘A’ but required in type ‘CC’. // cc =aa

//泛型兼容性 interface Empty { value:T } //只有在使用泛型接口的时候才能确定是否兼容 //如果泛型的类型相同,则兼容,反之

//泛型函数的兼容 let cs1 = (x:T):T =>{ console.log(x) return x } let cs2 =(y:U):U =>{ console.log(y) return y } cs1 =cs2

<a name="SUgrj"></a>
### 类型保护  instanceof   in关键字  typeof  创建类型保护函数
理解:    TypeScript能够在特定的区块中保证变量属于某种确定的类型<br />    可以在此区块中放心的引用此类型的属性,或者调用此类型的方法
```typescript
enum Type {
    Strong,
    Week
}
class Java {
    helloJava(){
        console.log("hello_Java")
    }
    java:string
}
class JavaScript{
    helloJavaScript(){
        console.log("Hello_JavaScript")
    }
    javascript:string
}
function isJava(lang:Java |JavaScript):lang is Java {
    return (lang as Java).helloJava !== undefined
}
function getLanguage(type:Type ,x:string|number) :Java|JavaScript{
    let lang = type === Type.Strong ? new Java() :new JavaScript()
    //instanceof  //区块 -------------------------------
    if (lang instanceof Java){
        lang.helloJava()
    }else{
        lang.helloJavaScript()
    }
    //in关键字 //区块 -------------------------------
    if("java" in lang){
        lang.helloJava()
    }else{
        lang.helloJavaScript()
    }
    //typeof //区块 -------------------------------
    if(typeof x === 'string'){
        console.log(x.length)
    }else{
        console.log(x.toFixed(2))
    }
    //类型保护函数 //区块 -------------------------------
    if(isJava(lang)){
        lang.helloJava()
    }else{
        lang.helloJavaScript()
    }
    return lang
}
getLanguage(Type.Strong,"2222")

高级类型:交叉类型& ,联合类型

交叉类型理解:使用&字符,多个类型的并集合
联合类型理解:使用 | 当一个变量类型不明确时,可使用| 连接, 那么变量的类型为多个里面的一个 交集

interface DogInterface {
    run():void
}
interface CatInterface {
    jump():void
}
//交叉类型,实现需要交叉类型的并集合
let pet: DogInterface &CatInterface ={
    run(){},
    jump(){}
}

//------------------------------------------------
//联合类型
let value : string | number  ="1212" || 123
//自变量联合类型
let b1 :"a" | "b" | "c"  //字符串联合类型  b1的值只能是a,b,c
let b2 :1 | 2 | 3 //数字联合类型 b2的值只能是1,2,3

//------------------------------------------------
//对象联合类型
class Dog_1 implements DogInterface {
    run(){}
    eat(){}
}
class Cat_1  implements CatInterface{
    jump(){}
    eat(){}
}
enum Master { Boy, Girl }
function getPet(master :Master) {
    let pet  =master === Master.Boy? new Dog_1() :new Cat_1()
    //对象联合类型只允许调用联合类型的共同成员 交集
    pet.eat()
    return pet
}

高级类型-索引类型 (有点模糊)

索引类型的操作符 keyof

let obj1 ={a:1, b:2, c:3 }

function getValues<T,K extends keyof T> (obj:T,keys:K[]):T[K][]{
    return  keys.map(key=>obj[key])
}
console.log(getValues(obj1,[ "a","b"])) //[1,2] 访问有的属性
console.log(getValues(obj1,[ "e","f"])) //[undefined,undefined] 访问有的属性   约束了

//索引类型  keyof T
interface Obj2 {
    a:number
    b:string
    c:any
}
let key : keyof  Obj2   = "a"
//索引访问操作符 T[K]
let value1 : Obj2['a'] = 1
//T extends U

高级类型-映射类型 旧的类型—>新的类型 同态类型

interface Obj {
    a:string
    b:number
    c:boolean
}
//映射为只读的类型
type ReadOnlyObj = Readonly<Obj>
type PartialObj = Partial<Obj>
//Ts自带的类型

高级类型-条件类型 T extends U ? X : Y (微懂)

理解: 如果类型T可以被赋值类型U 那么类型就是X 类型 否者为Y类型

// T extends U ? X  : Y
type TypeName <T> =
    T extends string ?'string':
    T extends number ? 'number':
    T extends boolean? 'boolean':
    T extends undefined? 'undefined':
    T extends Function? 'function':
        "object"
type T1 = TypeName<string>
type T2 = TypeName<number>
type T3 = TypeName<string[]>

//(A | B ) extends  U ? X : Y
type T4 = TypeName<string | string[] | number> //string object number

装饰器

属性装饰器 target 是构造函数原型
static 静态属性装饰器 target是构造函数本身
实例方法 三个形参, target: any propertyKey: string descriptor: PropertyDescriptor
参数装饰器

执行顺序的规律
1.类装饰器是最后执行的,后写的类装饰器先执行
2.方法和就去参数中的装饰器先执行参数3.就去和属性装饰器,谁在前面先执行谁
一般人内往外执行先内后外
类比组件componentDidMount 先上后下先内后外一定要学会知识的
koa中间件redux中间件

工程篇

ES6 与CommonJs模块系统

ES6 导入导出

//a.ts
//单独导出
export let a = 1
//批量导出
let b = 2
let c = 3
export {b, c}
//导出接口
export interface p {
    x: number
    y: string
}
//导出函数
export function fun(){
    //...
}
//别名导出
function fun1(){}
export { fun1 as fun2 }
//默认导出
export default function(){
    console.log("default")
}
//引入外部模块,重新导出
export {str as hello} from './b'

//b.ts
export const str ="hello"

导入

//批量导入
import { a,b,c} from "./a"
//导入接口
import {p} from "./a"
//导入时起别名
import {fun2 as F} from "./a"
//导入模块中的所有成员,绑定在All上
import  * as Al  from "./a"
//不加{} 导入默认
import myFunction from "./a"

CommonJs导入导出

module.exports     require

命名空间 解决全局污染问题 namespace 关键字

//a.ts
namespace Shape{
    export function circle(x:number){
        return x * x
    }
}
//b.ts
/// <reference path="./a.ts" />
namespace Shape {
    export function square(x: number) {
        return x + x
    }
}
// Shape.circle(2)
console.log(Shape.square(1));
//命名空间别名
import square = Shape.square
console.log(square(11));

声明合并

interface A {
    x:number
    foo(bar:number):number //5
    foo(bar:'a'):string //2
}
interface A {
    y:number
    foo(bar:string):string //3
    foo(bar:number[]):number[] //4
    foo(bar:'b'):number //1
}
//接口声明合并  可以相同属性,但是类型要一样
let a :A = {
    x:1,
    y:1,
    foo(bar: any) {
        return bar
    }
}

//命名空间,不允许重复定义相同名字属性
//命名空间可以与函数,类,枚举合并
//命名空间必须放在以上的后面

练习

  1. 数组对象声明 ```typescript type TTimeType = { state: string, duration: number

} // 选择的时间周期 const selectTime: TTimeType[] = [] ```