
javascript 动态弱类型语言
TypeScript的纯在,静态强类型语言诞生 类型思维
优缺点
优势:
- 提升代码质量
- 增强代码可维护性
- 提升开发效率
- 重塑类型思维
- …
成本.
- 思维转化
- 对接现有开发生态
- 项目迁移
- 接口,声明文件的维护
- ….
静态类型语言:在编译阶段确定所有变量的类型
动态类型语言:在执行阶段确认所有变量的类型
强类型语言:强制数据类型定义的语言,
也就说一旦定义了变量为某个数据类型,如果不通过强制类型转换,将无法更改类型
弱类型语言:数据类型可以被忽略的语言,
跟强类型相反
TS会在编码阶段进行类型检查
特性:
类型检查,
语言扩展,
工具属性:编译成javascript
基础篇
环境搭配
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
"start": "webpack-dev-server --mode=development --config ./build/webpack.config.js","build": "wbepack --mode=production --config ./build/webpack.config.js",
类型注解 :相当于强类型语言中的类型声明
语法:(变量/函数) :type 联合语法
// todo
元祖 数量和类型已知的数组
普通枚举
枚举 
// 常量枚举
any 如果变量为any 就跟js差不多, 不进行类型检查
null
let element: (HTMLElement | null) = document.getElementById(‘root’);
//非空断言 常用
element!.style.color = ‘green’;
// structNullChecks 则不能把null undefined 赋值为xxx


类型推导
//基本类型let bool: boolean = truelet number: number = 1212let str: string = '112121'//引用类型-数组let arr: number[] = [1, 2, 3]//泛型let arr2: Array<number> = [1, 2, 3]//联合类型let arr3: Array<number | string> = ['111', 222]//元组 数量和类型已知的数组let tuple: [number, string] = [1, 'string']//元组越界 可以添加,但是不能访问tuple.push('newString')// console.log(tuple)for (let A = 0; A < tuple.length; A++) {console.log(tuple[A])}//函数 形参必须定义类型let add = (x: number) => x//对象let obj: { x: number, y: number } = {x: 11,y: 22}//如果之定义了对象为object类型.不允许修改obj.x = 333// symbollet s1: symbol = Symbol()let s2: symbol = Symbol()console.log(s1===s2) //false//any类型 与js没有区别,let xx= 1x='222'x=[]//never类型let error =()=>{throw new Error('')}let endless= ()=>{while(true){}}
枚举类型 :一组有名字的常量集合

1.枚举的成员都是只读,不允许修改
2.不同枚举成员是不允许比较
3.开发中可以把常量抽取出来用作枚举形式
数组枚举 ,字符串枚举, 异构枚举, 枚举成员 ,常量枚举
//数组枚举 反向映射enum Num {one = 3, //3 默认从0开始 如果设置了值,会递增two,//3three = 10,//10four//11}console.log(Num)//字符串枚举 枚举定义第一个字符要大写enum Str {success = '成功',fail = '失败'}//异构枚举enum Answer {n,y='Yes',b= 'Bool',}//枚举成员 编译的时候已经知道结果enum Char {a,//没有定义值,默认数字0b=Char.a,//枚举的引用c=1+2,//表达式定义值//computed计算成员,运行时才执行d=Math.random(),e ='123'.length}//常量枚举 编译阶段会被移除 ,不需要对象,只需要对象的值const enum Month {Jan,Feb}let month = [Month.Jan,Month.Feb] //编译阶段不会又常量枚举
接口
理解:接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。
//定义接口里面的值interface List {readonly id: number //readonly 代表只读属性name: stringage?: number //?代表可选属性}//引用接口interface Result {data: List[]}//传入参数是一个接口对象function render(result: Result) {result.data.forEach(item => {console.log(item.id, item.name)if (item.age) console.log(item.age)})}let result = {data: [{id:1,name:'小明',age:20},{id:2,name:'小红'},]}render(result)//定义不确定的接口,全部都是字符串interface StringArray {[index:number]:string}let charts:StringArray = ['A','B','C']interface Names {[x:string]:string[z:number]:any}
异构接口
//别名定义函数 接口定义type Add = (x: number, y: number) => number//实例定义let addFun: Add = (a, b) => a + b//调用console.log(addFun(1, 2))//混合类型的接口interface Lib{():voidversion:string,doSomething():void}function getLib(version:string){let lib :Lib =(()=>{}) as Lib //断言lib.version =versionlib.doSomething = () => {console.log(`版本号:${lib.version}`)}return lib}let lib1 =getLib('1.0')let lib2 =getLib('2.0')lib2.doSomething()
函数定义汇总 自变量定义 别名定义 接口定义 剩余参数 函数重载
1.函数重载,编译的时候会创建函数重载列表
//函数定义function add1(x: number, y: number): number {return x + y}//自变量定义let add2: (x: number, y: number) => number//别名定义type add3 = (x: number, y: number) => number//接口定义interface add4 {(x: number, y: number): number}//调用函数参数个数必须相同,// 可在参数中添加?设置为可选参数,可选参数必须在必选参数在后function add5(x: number, y = 2, z: number, q = 1) {return x + y + z + q}console.log(add5(1, undefined, 3))//剩余参数function add6(x: number, ...args: number[]) {return x + args.reduce((prev, cur) => prev + cur)}console.log(add6(1, 2, 3, 4, 5, 6)) //21//函数重载 利用参数类型实现重载function heavyLoad(...args: number[]): numberfunction heavyLoad(...args: string[]): stringfunction heavyLoad(...args: any[]): any {let first = args[0]if (typeof first === 'number') {return args.reduce((prev, cur) => prev + cur)} else if (typeof first === "string") {return args.join("_")}}console.log(heavyLoad(1, 2, 3, 4, 5))console.log(heavyLoad('1', '2', '3'))
类 class 会覆盖es6的类 创建,class 继承 extends
class Dog {constructor(name: string) {this.name = name}name: stringsayHi() {console.log(`我叫${this.name}`)}run() {}}//类的成员在实例上面,不在原型上let dog = new Dog("旺财")dog.sayHi()//继承class Husky extends Dog {constructor(name: string, age: number) {super(name)this.age = age}age: numberplay() {console.log(`我叫${this.name},我会卖萌玩耍,我有年龄`)}sayHi1 = () => {console.log('我有年龄' + this.age)}}let husky = new Husky('哈稀奇', 14)husky.sayHi1()husky.sayHi()husky.play()
修饰符
1.public
2.private
3.protected
4.readonly
5.static
抽象类, 只能被继承,不能被实例 多态
// 创建抽象类 相当于把公共的东西抽到同一个抽象类里面abstract class Animal {eat() {console.log('eat')}// 抽象方法 继承抽象类一定要有实现abstract sleep():void}class Dog1 extends Animal {constructor(name: string) {super()this.name = name}name: stringsleep(): number {console.log('Dog_sleep')return 1}}let dog1 =new Dog1("哈士奇")dog1.eat()dog1.sleep()//多态实现class Cat extends Animal{sleep(): void {console.log("cat_sleep")}}let cat = new Cat()let animals:Animal[]= [dog1,cat]animals.forEach(item=>{item.sleep()})//this.链式操作class WorkFlow{step1(){return this}step2(){return this}}new WorkFlow().step1().step2()//父类与子类都能链式操作class MyFlow extends WorkFlow{next(){return this}}new MyFlow().step2().next().next().step1()
类与接口之间的关系
- 类实现接口必须声明接口的所有成员
- 接口只能约束类的共有成员
- 接口之间能相互继承,多继承(,分割)
- 接口继承类

interface Human {name: stringeat(): void}//类继承接口,需要把所有接口成员实现class Asian implements Human {constructor(name: string) {this.name = name}name: stringeat(): void {console.log("Eat_Fun")}sleep(): void {console.log("sleep_fun")}}interface Man extends Human {run(): void}interface Child {cry(): void}// 接口之间的继承关系interface Boy extends Man, Child {age: number}let boy: Boy = {name: "杨明",age: 27,run() {},eat() {},cry() {console.log(this.name + "crying")return this}}boy.cry()//接口继承类class Auto {state:number =1}interface AutoInterface extends Auto{//接口里面的成员interfaceState: number}class C implements AutoInterface{constructor(name:string,state:number) {this.name = namethis.state =state}name:stringstate:numberinterfaceState =2}//实例let c = new C("小明",1)class Bus extends Auto implements AutoInterface{constructor(interfaceState:number) {super();this.interfaceState =interfaceState}interfaceState:number}let bus = new Bus(1)console.log(bus)
泛型(重要概念) 函数或者类支持多种数据类型
泛型理解:不需要预先确定的数据类型,具体的类型在使用的时候才确定
//泛型函数的定义 ,定义时不知道参数类型,返回值类型 但是两个类型必须一致function log<Type>(value: Type): Type {console.log(value)return value}//1.调用log<string>("小明") //调用前定义类型log("小明")//2.别名type Log =<Type>(value:Type) => Typelet myLog:Log = logmyLog("小明")//************************************************************************//泛型接口interface Log<Type = string> {(value: Type): Type}let mylog :Log<number> = logmylog(1)
泛型的好处:
- 函数和类可以轻松地支持多种类型,增强程序的扩展性
- 不必写多条函数重载,冗长的联合类型声明,增强代码可读性
-
泛型的约束
// 泛型函数class Log <Type> {run(value:Type) :Type{console.log(value)return value}}let log1 = new Log<number>()log1.run(1111)let log2 =new Log()log2.run({a:11})//***************************************// 泛型的约束interface Length {length:number}//传入泛型参数必须有length属性function log<Type extends Length> (value:Type) :Type {console.log(value, value.length)return value}log([])log("123")
类型检查机制
理解:TypeScript编译器在做类型检查时,所秉承的一些原则,以及表现出的一些行为
作用:辅助开发,提高开发效率 类型推断
- 类型兼容性
- 类型保护
类型推断
理解:不需要制定变量的类型(函数的返回值类型),TypeScript可以根据某些规则自动地为其推断出一个类型
1.基础类型推断
2.最佳通用类型推断
3.上下文类型推断//基础类型推断 从右到左推断let any //any类型let a = 1 //numberlet b = "1231231" //stringlet c1 = (x = 1) => x + 1 //number :number//最佳通用类型推断let arr1 = [1, null, "123"] //(number | null | string)[]//上下文类型推断window.onkeydown = (event) => {//console.log(event.buttons)}
类型兼容性
理解:当一个类型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
//泛型函数的兼容
let cs1 =
<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
}
}
//命名空间,不允许重复定义相同名字属性
//命名空间可以与函数,类,枚举合并
//命名空间必须放在以上的后面
练习
- 数组对象声明 ```typescript type TTimeType = { state: string, duration: number
}
// 选择的时间周期
const selectTime: TTimeType[] = []
```
