【持续更新】typecsript语法

在这里插入图片描述

简介

介绍
typescript是微软开发的一款编程语言;
是js的超级,遵循最新的es6、es5规范,扩展了js的语法;
更像后端java、c#这样的面向对象语言,可以让js开发大型企业项目;
angular2+基于ts语法,最新的vue、react也集成ts

缺点: 编辑器会信息量过载

工程配置

全局下载:npm i typescript -g
查看版本:tsc -v
编译生成js文件:tsc  index.ts
查看帮助:tsc -h
生成配置文件:tsc --init

ts-loader 使ts能在webpack使用
ts-node 直接运行ts,不用编译 (ts index.ts)

vscode自动化编译

tsc index.ts 不会按tsconfig.json配置去编译,而是按默认自带的配置项;
只输入tsc的会按tsconfig.json配置去编译
用ts-node index.ts会按tsconfig.json配置去编译

//  1)生成配置文件tsconfig.json
tsc  --init 

// 2)修改配置
"module": "amd",
"outDir": "./js",

// 3)启动监视任务:终端 -> 运行任务 -> 监视tsconfig.json  

// 4)编译完后,启动node index.js或者把index.js放到html里查看

在这里插入图片描述

// tsofig.base.json
{
    "files":["src/a.ts"], //编译单个文件的列表
    "include":["src/*"], //需要编译的文件或目录  加通配符*:只会编译src目录下的文件  或者在下一层 src/*/*
    "exclude":["lib"], // 排除
    "references":[
      {"path":"../src/client"},
      {"path":"../src/server"},
    ]
}
// tsconfig.json
{
   "extends":"./tsonfig.base" // 导入基础配置
   "compilerOptions": {
    "incremental": true, // 增量编译 可以在第一次编译后生成存储编译信息的文件,在二次编译的时候根据这个文件做增量编译,这样就可以提高编译速度
    "tsBuildInfoFile":"./buildFile" // 增量编译文件的存储位置
    "diagnostis":true, // 打印诊断信息
    
    "target": "es5", // 编译成的目标语言时什么版本,默认编译为es3
    "module": "commonjs", // 生成代码的模块标准 'none', 'commonjs', 'amd', 
    "outFile": "./app.js",  // 将多个相互依赖的文件生成一个文件,可以用AMD模块中
    
    "lib": ["es2019.array"],   // TS需要引用的库,即声明文件,默认"dom","es5","scripthost"

    "allowJs": true,   // 允许编译js文件(js、jsx)
    "checkJs": true, // 允许在js文件中报错,通常与allowJS一起使用
    "outDir": "./js", // 指定输出目录
    "rootDir": "./", // 指定输入文件目录(用于输出)
   
    "declaration": true,   // 生成声明文件
    "declarationDir":"./d",  // 声明文件的路径
    "emitDeclarationOnly": true,  // 只生成声明文件
    "sourceMap": true,  // 生成目标文件的 sourceMap
    "inlineSourceMap": true, // 生成目标文件的 inline sourceMap
    "declarationMap": true,  / 生成目标文件的 sourceMap
    "typeRoots": [],    // 声明文件目录,默认node_modules/@types
    "types": [],   // 声明文件包

    "removeComments": true, // 删除注释

    "noEmit": true,     // 不输出文件,只做类型检查
    "onEmitOnError":true,  // 发生错误时不输出文件

    "noEmitHelpers":true, // 不输出文件
    "importHelpers": true, 

    "downlevelIteration": true, // 降级遍历器的实现(es3/5)

    "strict": true,  // 开启所有严格的类型检查           
    "alwaysStrict": true, // 在代码中注入"use strict"
    "noImplicitAny": true,   // 不允许隐式的any类型
    "strictNullChecks": true,   // 不允许把null、undefined 赋值给其他类型变量
    "strictFunctionTypes": true,   // 不允许函数参数双向协变
    "strictBindCallApply": true,  // 严格的bind/call/apply检查
    "strictPropertyInitialization": true,  // 类的实例属性必须初始化
    "noImplicitThis": true,   // 不允许this有隐式的any类型

    "noUnusedLocals": true,  // 检查只声明,未使用的局部变量
    "noUnusedParameters": true,  // 检查未使用的函数参数
    "noImplicitReturns": true,  // 每个分支都要有返回值
    "noFallthroughCasesInSwitch": true, // 防止switch语句贯穿

    "esModuleInterop": true,  // 允许export=导出,由import from导入
    "allowUmdGlobalAccess": true, // 允许在模块中访问UMD全局变量
    "moduleResolution": "node", // 模块解析策略
    "baseUrl": "./",    // 解析非相对模块的基地址             
    "paths": {"jquery":["node_modules/jquery/dist/jquery.slim.min.js"]},    // 路径映射,相对于baseUrl                
    "rootDirs": ["src", "out"],  // 将多个目录放在一个虚拟目录下,用于运行时

    "listEittedFiles": true, // 打印输出的文件
    "listFiles": true // 打印编译的文件(包括引用的声明文件)

    "jsx": "react"   // preserve 生成的代码会保留jsx格式(扩展名就是jsx),可以被后续操作继续转换使用  react-native 生成的代码是jsx格式(扩展名是js) react 生成的代码不会保留jsx,而是存在jsx语法
  }
}

tsc ./src/a.ts -t es6   // target 编译成es6版本的语言     -t 是 --target的简写
tsc ./src/a.ts -m amd   // module 编译成amd模块

tsc -b src/server  --verbose  // -b为编译

webpack上配置

如何让transpileOnly开启时又可以做类型检查呢?
fork-ts-checker-webpack-plugin -D 插件,会把类型检查放在一个独立的进程中进行

const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

const webpackConfig = {
  module: {
    rules: [
      {
        test:/\'tsx?$/i,
        use:[{
          loader:"ts-loader", // 语言转换,类型检查
          options:{
            transpileOnly:true  // 只做语言转换,不做类型检查,可以加快构建速度
          }
        }],
        exclude: /node_modules/
      }
    ]
  },
  plugins: [new ForkTsCheckerWebpackPlugin()]
};

数据类型

定义时必须指定类型(有利于维护,增加类型校验)

基本类型

包括:number、boolean、string、symbol、void、null和undefined,以及所有用户定义的枚举类型

  • 数字类型number:用来表示双精度64位格式IEEE754浮点值
var num:number=123
num=456
  • 布尔类型boolean:用来表示true或false的逻辑值
var flag:boolean = true
flag = false
  • 字符串类型string:用来表示存储为unicode UTF-16的字符序列
var str:string='hhhhh'
  • Symbol:用来表示对象属性的键,键都是唯一的

  • void类型:表示没有任何类型,一般用于定义的方法没有返回值。可能是null和undefined
    void类型是任意类型的子类型,是null和undefined类型的超类型
    注意:不允许声明void类型的变量,因为没有用处。但是可以用来作为泛型类型或函数的类型参数

function run(){...} // es5的定义

function run():void{...}
  • null:代表应用Null类型的值,无法直接引用Null类型本身
    是除了undefined类型外的所有类型的子类型
var n:number = null
var x=null // 等于 x:any = null
var e:Null // 报错,无法引用Null类型
var num:undefined

// 定义没有赋值就是undefined
var num:number | undeinfed 

var num:null

// 一个元素可能是number类型,可能是null,可能是undefined
var num:number | null | undefined
num=123
  • undefined:表示未初始化变量的值,无法直接引用Undefined类型本身
    是所有类型的子类型,可能是所有基本类型、对象类型、联合类型、交集类型和类型参数的有效值
var n:number  // 等于 n:number = undefined
var x=undefined // 等于 x:any = undefined
var e:Undefined // 报错,无法引用Undefined类型
  • 枚举类型enum:可以定义一些带名字的常量
    枚举类型的值都是数字类型,因此又被称为数字枚举

是对js标准数据类型的补充,可以为一组数值去赋值对应的名字
enum 枚举名{标识符[=整数常数], 标识符[=整数常数], 标识符[=整数常数]… }

// 1. 数字枚举
enum Flag { 
    success,  // 也可以定义 success = 1
	error ,   
	warn
}
let a:Flag=Flag.success 
let b=Flag.error
let c=Flag.warn

b = 2
console.log(a, b, c) // 0 2 2
// 提供反查能力
let flagName:string = Flag[2]  // error

// 被编译成
var Flag 
(function(Flag ){
	Flag[Flag['success']=0] = 'success';
	Flag[Flag['error']=2] = 'error';
	Flag[Flag['warn']=2] = 'warn';
})(Flag ||(Flag ={}))


/* 操作:
	位运算
	|合并标记
	|=添加一个标记
	&=和~来清理一个标记
*/
enum Flag { 
    success = 0,  
	error = 1 << 0 , // 1
    warn = 1 << 3, // 8
    other = success|error|warn  // 相加了
}
let a=Flag.success 
let b=Flag.error 
let c=Flag.warn 
let d=Flag.other 

console.log(a, b, c, d) // 0 1 8 9
// 2. 字符串枚举
enum Flag { 
    SUCCESS='success',  
	ERROR='error' , 
	WARN='warn'
}

const someString = 'success'
const value = someString as Flag
if(value === Flag.SUCCESS){
    console.log(value)  // success
}
  • never类型:代表从不会出现的值,是其他类型(包括null和undefined)的子类型
var a:never 
a=123 // 错误
a=(()=>{
	throw new Error('错误')
})()

对象类型

包括:类、接口类型、数组类型、元组类型、函数类型、构造函数类型

  • 数组类型array
    • number[]
    • Array 数组泛型
// var arr=['1','2'] // es5定义

// 1.第一种定义方式
var arr:number[] = [11,22,33]

// 2.第二种定义方式
var arr:Array<number> = [11,22,33]
  • 元组类型
    属于数组的一种,可包含多种类型元素的数组
    和any不一样,可以约束类型
// 给每个位置指定一个类型
let arr:[number, string]=[123,'this is ts']

// 当添加越界的元素时,它的类型会被限制为元组中每个类型的联合类型
arr.push('true')
arr.push(true)   // 报错
arr.push(23)
  • object
    TypeScript 2.2 引入了被称为 object 类型的新类型,用于表示非原始类型。
// 使用object类型声明一个函数类型
declare function create(o: object | null): void

create({ prop: 0 })
create(null)
create(string)  // 报错

任意类型

作用:选择退出类型检查,并让这些值通过编译器的检查

  • 用any表示任意类型
    表示允许赋值为任意类型
// 1. any类型的变量可以赋任意类型的值
let b:any = 123
b='abc'

// 2. any类型的变量可以使用任意属性和方法
let b:any = 3
console.log(b.length)

// 3. 定义不指定类型的遍历,且不赋值时相当于any
let b  // 此时b为any类型
b=123 

let b=123 // b已经赋值,为unmber类型
b='123' // 报错,b已经赋值
  • 用Object表示任意类型
let notSure:any = 4
notSure.ifExitsts() 
notSure.toFixed()

let prettySure:Object = 4
prettySure.toFixed() // 错误,toFixed()不是Object类型的方法

继承

允许继承来扩展类

// 超类、父类、基类是被继承的类Animal,子类、派生类是继承于超类的类Dog
class Animal{
    name:string
    constructor(name:string){
        this.name = name
    }
    move(distance:number=0){
        console.log(`Animal moved ${distance}m`)
    }
}

class Dog extends Animal{
    constructor(name:string){
        super(name)
    }
    move(distance:number=5){
        super.move(distance) // 调用父类方法
    }
}

let sam = new Dog('sam')
sam.move(34)

修饰符

public:公有(在当前类里面、子类、类外面都可以访问)
protetced:保护类型 (在当前类里面、子类里面可以访问,实例的没法使用)
private:私有(只在当前类里面可以访问)
readonly 与词同意,只读
static 静态属性或方法

class Person{
	private name:string
	constructor(name:string){
        this.name = name
	}
}
new Person('xiaoming').name // 报错
class Person{
	readonly name:string
	constructor(name:string){  // 或者 参数属性 constructor(readonly name:string){} 
        this.name = name
	}
}

var p = new Person('lili')
p.name = 'xiaoming'   // 报错

存储器

ts可以用getter setter对对象成员访问,比如设置时做额外逻辑。编译后可以看出是利用Object.defineProperty来实现getter和setter

class Employee{
    private _fullName: string
    constructor(){
        this._fullName = "";
      }
    get fullName():string{
        return this._fullName
    }
    set fullName(newName:string){
         // 额外逻辑处理.....
        this._fullName = newName
    }
}

let employee = new Employee()
employee.fullName = 'xiaoming'   
console.log(employee.fullName)

静态属性和方法

function Person(){
    // 实例方法 要new之后才可以调用,原型上的属性会被多个实例共享,构造函数不会
	this.run=function(){}
}
Person.run=function(){} // 静态方法
Person.name = 'hahaha' // 静态属性
class Person{
	public name:string;
	static age="18"
	constructor(n:string){
		this.name = n
	}
	run():void{ return `${this.name}` }
	// static print(){  console.log(this.name) } // 没办法直接调用类的属性,除非增加静态属性
	static print(){  console.log(this.age) }
}
Person.print() 
  • 与es6 class的区别是:ts的class中for in 可以获取隐式原型上所有属性和方法。Object.keys()和Reflect.ownKeys()获取不了。
  • 如果要添加私有属性,要用Symbol值
const bar = Symbol('bar');
export default class myClass{
  // 公有方法
  foo(baz) {
    this[bar](baz);
  }
  // 私有方法
[bar]() {
     return '22222'
}
  // ...
};

for(var key in Axios){
  console.log(key)    // 都获取不到私有
}
Object.keys(Axios) // 取不到属性和方法
Reflect.ownKeys(Axios) // 取不到属性和方法

多态

父类定义一个方法不去实现,让继承的子类去实现,每个子类有不同的表现
多态属于继承

class Animal{
	name:string;
	constructor(n:string){
		this.name=name 
	}
	eat(){ console.log('xxx') } // 具体吃什么?让继承他的子类去实现,每个子类的表现不一样
}

class Dog extends Animal{
	constructor(n:string){ super(n) }
	eat(){
		return this.name + 'xxx'
	}
}

class Cat extends Animal{
	constructor(n:string){ super(n) }
	eat(){
		return this.name + 'xxxxxxx'
	}
}

抽象类

它是提供其他类继承的实例,不能直接被实例化,只能给它的派生类使用
用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须再派生类中实现

  • abstract抽象方法只能放在抽象类里面
  • 抽象类和抽象方法用来定义标准,要求它的子类必须包含某个方法
abstract class Animal{
    name:string
    constructor(name:string){
        this.name=name
    }
    // 抽象方法约束了类里面定义的方法属性
    // 只有方法的描述,没有方法体的实现
    abstract eat():void 
    run():void{...}
}

// const cat= new Animal() // 报错,派生类才能使用

class Dog extends Animal{
	constructor(n:any){ super(n) } // 在派生类的构造函数中必须调用 super()
    // 必须要实现,否则会报错
    eat(){
        return `${this.name} eating`
    }
    shout(){
        return `${this.name} shouting`
    }
}
let d:Animal
d = new Dog('小狗')
d.eat()
d.run()
d.shout() // 报错,因为已经给d声明了Animal类型
  • 抽象方法:有abstract,没有方法体,不能创建实例(抽象方法)
  • 实现方法:没有abstract、有方法体、可以创建实例

接口 Interfaces

  • 对对象进行描述
  • 对类的一部分行为进行抽象
// 对批量方法传入参数进行约束
接口:行为和动作的规范,对批量方法进行约束
interface FullName{
	firstName:string; // 注意分号;结束
	secondName:string;
}
function printName(){
	// 必须传入对象  firstName  secondName
	console.log(name.firstName+'-------'+name.secondName)
}

printName({age:20,firstName:'张三',secondName:'李四'}) // 报错,要求必须只有firstName、secondName
// 这样不会报错
var obj={age:20,firstName:'张三',secondName:'李四'}  // 这样写,要求包含两个
printName(obj)

接口

接口:在面向对象语言中,是n个行为的抽象/描述,但是没有实现,由类去实现

  • 不会检测属性顺序,只要存在这个属性,类型对应就可以
  • 对属性、函数、类起到限制和规范

可选、可读属性

  • 可选属性:?
  • 只读属性:readonly
// 使用接口描述属性和方法
interface Person{
	readonly id:number;
	name:string;
	sex?:string;
}
const p1:Person={
	id:1,
	name:'xiaoming',
	sex:'男'
}
p1.id=2  // 报错

误拼报错问题

interface Square{
    color: string
    area: number
}

interface SquareConfig{
    color?:string
    width?:number
    // [propName:string]:any // 加索引性,可以有适当属性,只要不是color、width就可以
}

function createSquare(config: SquareConfig): Square{
    let newSquare = {color:'white', area:10}
    if(config.color){
        newSquare.color = config.color
    }
    if(config.width){
        newSquare.area = config.width * config.width
    }
    return newSquare
}

// 误拼,报错问题
// createSquare({colour:'balck', width:100} as SquareConfig) // 1.可以用类型断言
// createSquare({colour:'balck', width:100}) // 2.加索引性,这种比较好

// 3.绕过它,但是没有意义
let obj = {colour:'balck', width:100}
createSquare(obj) 

索引签名(可索引类型)

可以用字符串访问js中的对象。注意js在任何一个对象索引签名上都有隐式调用toString方法,索引签名必须是string或unmber类型

let obj ={
    toString(){
        console.log('toSting called')
        return 'hello'
    }
}
let foo:any={}
foo[obj.toString()] = 'world' // toSting called
console.log(foo['hello']) // world

声明一个索引签名
注意:所有成员都必须符合索引签名

// 1
const foo:{[index: string]:{message:string}}={}
foo['a']={message:'123', name:'sss'}  // 报错 name没有在类型里

// 2. 类型都要匹配
interface Foo{
    [key:string]:number,
    x:number,
    y:string // 报错,与索引名的类型不匹配,y必须是number类型
}

// 3. 不在索引名类型上也报错
type Index = 'a'|'b'|'c'
type FromIndex = {[k in Index]?:number}
const good:FromIndex = {b:1,c:2,d:555} // 报错,d不在FromIndex类型上


// 4. 数组形式
interface StringArray {
    [index:number]:string   //用数字做索引,返回值是string
}
let myArray:StringArray = ['xiaoming', 'lili']
myArray[0]


// 5. 可以设置为只读
interface readonlyPerson{
    readonly [index:number]: string
}
let aPerson :readonlyPerson = ['xiaoming', 'lili']
aPerson[2]= 'xiaohong' // 报错

继承接口

和类一样,接口也是可以互相继承的,并且一个接口可以继承多个接口:

interface IA {
  a: string;
}

interface IB extends IA {
  b: number;
}

let exten: IB = {
  a: "1",
  b: 1
}

// 2. 接口继承接口
interface LightableAlarm extends Alarm, Light{...} 
  • 继承接口类
    当接口继承一个类时它将会继承类的所有成员但不包括实现,会继承私有的,受保护的成员
class Base {
  public type: string;
  constructor(type: string){
    this.type = type;
  }
  logType(){  return this.type; }
}

interface IButton extends Base {}

class Button implements IButton {
  type = "button"
  constructor(){}
  logType(){
    return this.type;
  }
}

对象类型

使用接口来定义对象的类型

interface LabelValue{
    label: string
}

function printLabel(labelObj:LabelValue){
    console.log(labelObj)  // {size:10, label:'哈哈哈~~~'}
}

let myObj = {size:10, label:'哈哈哈~~~'} // 必须要有label这个属性,并且类型必须一致

printLabel(myObj)

函数类型接口

interface SearchFunc{
    (source: string, subString:string):boolean
}
// let mySearch:SearchFunc = function(src:string, sub:string):boolean{
//     return src.search(sub) >-1
// }
let mySearch:SearchFunc = function(src, sub){ // 也可以简写,让ts自行推断
    // return src.search(sub) >-1
    return sub  // 报错
}

class类类型接口

强制一个类去符合某种契约

// 1. 定义一个类去实现接口
interface ClockInterface{
    currentTime: Date  //  类属性分为静态类型和实例类型
    setTime(d:Date): any
}

class Clock implements ClockInterface{
    currentTime = new Date()
    constructor(h:number, m:number){}
    setTime(d:Date){
        this.currentTime = d
    }
}

//  2. 一个类可以实现多个接口
class Car implements Alarm, Light{....}

多实现,单继承(可以实现并继承多个接口,但是只能继承一个类)

/* 1. 一个类可以实现一个接口
 一个类可以继承于另一个接口
 */
interface BtnProp{
	click():any
}
class BaseBtn{}
class MyBtn extends BaseBtn implements BtnProp{
	// 不实现接口就报错,必须写,要一致
	click(){
		console.log('xxxxx')
	}
}

混合类型

interface Counter{
    (start:number):string // 函数类型接口
    interval:number
    reset():void  // 函数类型接口
}

function getCounter(): Counter{
    let counter = (function(start:number){

    }) as Counter
    counter.interval = 123
    counter.reset = function(){

    }
    return counter
}

let c = getCounter()
c(10)
c.interval = 5.0

函数

函数声明、可选参数、默认参数、剩余参数

// 1. 函数声明
function run(a:string, b:number):string{ return '123' } 

// 2. 函数表达式
var fn=function(a:string, b:number):number{ return 123 } 
var fn=(a:string, b:number):number=>{ return 123 } 

// 3. 可选参数
function getInfo(name:string, age?:number):string{ 
	return `${name}----${age}`
}

// 4. 形参默认值
function getInfo(name:string, age:number=18):string{ 
	return `${name}----${age}`
}

// 6. 剩余参数(三点运算符)
function sum(...result:number){...}
function sum(a:string,b:number,...result:number){...}

// 7.函数做为参数
let myadd:(value:number, increment:number) => number = function(x:number, y:number):number =>{
return x+y
}

this

ts编译出来的是严格模式"use strict",this为undefined

// 1. 使用箭头函数
let obj = {
    name: 'xiaoming',
    getName: function() {
      return () => this.name
    }
}
obj.getName()()

// 2. 使用call来改变this时,要给它一个明确的类型
class Call {
    say() {
        console.log('hello world!~~~')
    }
}
const call = new Call();
const func = function (this: Call) {
    this.say();
}
func.call(call);  // hello world!~~~

函数重载

通过为同一个函数提供多个函数类型定义来实现多种功能的目的

  • 对于不同的参数返回不同的结果
  • es5中重名,会替换之前的方法
function getInfo(name:string):string
function getInfo(age:number):string
function getinfo(str:any):any{
	if(typeof str==='string'){
		return '我叫:'+str
	}else{
		return '我的年龄是'+age
	}
}
function getInfor(name:any, age?:any):any{
	if(age){
		return '我叫:'+name+'我的年龄是'+age
	}else{
		return '我叫'+name
	}
}

泛型

是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性

  • 开始不确定具体类型,使用再指定,让参数类型和返回类型一致
// T用于捕获参数类型
function createArray<T>(length:number, value:T):Array<T>{
	let result:Array<T>=[]
	for(let i=0;i<length;i++){
		result[i] = value
	}
	return result
}
createArray<string>(3,'x') // ['x','x','x']
createArray<number>(4, 555) // [555, 555, 555, 555]

// 另一种调用方法
// 利用了类型推论,编译器会根据传入的参数帮我们确定T的类型,保持代码精简
createArray(4, 555) 

泛型变量、类型、接口

  1. 泛型变量
    arg是任意类型的,如果想访问参数的长度 ,可以把T作为数组
function identity<T>(arg:T[]):T[]{
	console.log(arg.length)
	return arg
}
// const func:()=>string = ()=>{return '123'}
const func:<T>()=>string = <T>()=>{return '123'}
  1. 泛型类型
function identity<T>(arg:T):T{
	return arg
}
// <T>(arg:T)=>T 称为泛型函数的类型
let myIdentity:<T>(arg:T)=>T = identity 
// 也可以用对象字面量来命名
let myIdentity:{<T>(arg:T) : T}= identity 
  1. 泛型接口
// 把上面的对象字面量拿出来,改造成泛型接口
interface GenericIdentityFn{
	<T>(arg:T):T
}
let myIdentity:GenericIdentityFn = identity


// 改造:把<T>拿出来作为借口参数,好处:其他成员也可以使用这个参数类型,调用的时候就知道使用什么类型
interface GenericIdentityFn<T>{
	(arg:T):T
}
// 使用的时候就要指定什么类型<number>
let myIdentity:GenericIdentityFn<number> = identity

泛型类

泛型类指实例部分,静态属性不能使用泛型类

class GenericsClass<T>{
	value:T
    public add?: (x: T, y: T) => T;
}

const cls = new GenericsClass<number>();
cls.add = (x: number, y: number): number => {
  return x + y;
}

泛型约束

  • 通过接口来约束T,意思是这个泛型要有接口里面的东西
interface Lengthsize{
	length:number
}
// 通过接口Lengthsize来约束T
function identity<T extends Lengthsize>(arg:T):T{ 
	console.log(arg.length)
	return arg
}
identity({length:3})
  • 通过keyof对参数做约束(对接口属性值做一个遍历)
/* 查询对象中某个属性的值,用T来约束,
希望key存在对象中,所以要对属性key也做约束,用K
K extends keyof T :K被T所约束
*/
interface Person{
	name:string
	age:number
	gender:string
}
/* 遍历
	type T='name'
	key: 'name'
	Person['name']

	type T = 'age'
	key: 'age'
	Person['age']

	...
*/

class Teacher{
	constructor(private info: Person){}
	getInfo<T extends keyof Person>(key:T):Person[T]{
		return this.info[key]
	}
}
const teacher = new Teacher({
	name: 'xiaoming'
	age: 18
	gender: 'male'
})
const test = teacher.getInfo('name')
  • 在泛型中使用类类型
/* 
    通过泛型创建工厂函数
    {new():T} => 因为是构造器类型,所以是new(),返回T,T代表类的实例类型
*/
function create<T>(c:{new():T}):T{  // 相当 c:new()=>T 都可以使用
    return new c()
}

类型注解(类型推断、类型断言、类型保护)

  • 类型推断:ts会自动尝试分析变量的类型
    如果ts能自动分析变量类型,就什么也不需要做,如果ts无法分析变量类型,就需要使用类型注解/断言
    ts会在没有明确的指定类型的时候推测出一个类型
    定义变量时赋值,推断为对应类型
    定义变量时没赋值,推断为any类型

类型断言 Type Assertion

ts允许你覆盖已有的推断,以你想要的方式分析它并手动指定一个值的类型
- <类型> 值
- 值 as 类型(tsx中只能用这种方法)

// <类型> 值      
function getLength(x:number|string){
	if((<string>x).length){   // 即 x as string
		return (<string>x).length
	}else{
		return (<string>x).toString().length
	}
}

----------------
let foo:any
let bar =<string>foo

还可以用<string>但是再jsx中存在歧义
let foo=<string>bar;</string>


// as
interface Foo{
	bar: number
	bas: string
}
const foo ={} as Foo
foo.bar = 123
foo.bas = 'hello'

双重断言

// 先断言成兼容所有类型的any,让编译器不再报错
function handler(event:Event){
	const element=(event as any) as HTMLElement
}

类型保护

联合类型出现的问题,才需要做类型保护

  1. 类型断言 as
interface Bird{
	fly: boolean;
	sing: ()=>{}
}
interface Dog{
	fly: boolean;
	sing: ()=>{}
}

function traninAnial(animal: Bird|Dog){
	if(animal.fly){
		(animal as Bird).sing()
	}else{
		(animal as Dog).bark()
	}
}
  1. in 检查一个对象上是否存在某个属性
function traninAnial(animal: Bird|Dog){
	if('sing' in animal){
		animal.sing()
	}else{
		animal.bark()
	}
}
  1. typeof用来捕获变量、类成员的类型(做类型约束和类型保护)
function add(first:string|numbr, second:string|number){
	if(typeof first === 'stirng' || typeof second === 'string'){
		return `${first}${second}`
	}
	return first + second
}
  1. instanceof 判断左边对象是否右边实例出来的
class NumberObj{
	count: nunmber
}
function addSecond(first: object|NumberObj,  second: object|NumberObj){
	if(first instanceof NumberObj && second instanceof NumberObj){
		return first.count + second.count	
	}
	return 0
}
  1. type 字面量做类型保护
    可以使用===、==、!==、!=来区分字面量类型
type state = 'yes'|'no'|'unknown'
function logOut(){
	if(state == 'yes'){ ...
	}else{...}
}
  1. strictNullChecks编译属性下的null和undefined
    strictNullChecks:不允许把null、undefined 赋值给其他类型变量
    可以使用==null 和 !==null来区分null和undefined
function foo(a?:number | null){
	if(a == null) return
	// a现在是number类型的
}
  1. 使用自定义的类型保护
interface Fish{
	swim: ()=>{}
}
interface Bird{
	fly: boolean;
	sing: ()=>{}
}

// 如果返回true,那pet就是Fish类型
function isFish(pet:Fish|Bird):pet is Fish{
    return (pet as Fish).swim !==undefined
}
if(isFish(pet)){
	pet.swim()
}else{
	pet.fly()
}
  1. 类型保护和回调函数
    ts中并不能假设类型保护在回调中一直有效,只需要把推断的安全值存放在本地的局部变量中,这可以自动确保它不会再外部被更改
// 类型保护
if(foo.bar){
	console.log(foo.bar.baz) 
	const bar = foo.bar
	function doingSome(()=>{
		console.log(foo.bar.baz) // 报错,对象可能是undefined
		console.log(bar.baz) // 正确
	})
}

类型别名 type

  • 类型别名:给一种类型起个新名字(常用于联合类型)
type A= string;
type B= ()=>string;   //此为函数类型形状,注意跟下面区分
type C= {name:string, age:number}
type str = A & B;  // 合并继承  或者联合 |
  • 字符串字面量
type Name = "xiaoming"|"xiaohong"|"xiaozhang";
let theName:Name = "xiaozhang";

高级类型

联合类型 Union Types

表示取值可以为多种类型中的一种,用 | 表示或者 T | U

let c:number | string =3   // 可以为number或者string
c='abc'

function toString(x:number|string){
    return x.toString()
}

// 返回接口
interfac Bird{...}
interfac Fish{...}
function getContent():Fish|Bird{ ... }

交集/交叉类型

将多个类型合为一个类型: T & U

interface A{a:number}
interface B{b:number}

let ab:A&B = {a:1, b:1}
let a:A = ab
let b:B = ab


type F1 = {a:string, b:string} => void
type F2 = {a:number, b:number} => void
var f1: F1&F2 =(a:string|number, b:string|number)=>{}
f('hello', 'world') // 正确
f(1, 2) // 正确
f(1, 'test') // 错误
// 把两个对象合并成新对象,返回T&U
function extend<T, U>(first:T, second:U):T & U{
    let result = {} as T & U
    for(let id in first){
        result[id] = first[id] as any
    }
    for(let id in second){
        if(!result.hasOwnProperty(id)){
            result[id] = second[id] as any
        }
    }
    return result
}

可以为null的类型

null和undefined是其他类型的有效值

// 1. 可选的,赋值可用undefined
function f(a:number, y?:number){...}
f(12, undefined)
f(12, null) // 报错,null不能赋值给number或undefined
f(undefined,12) // 报错
class C{
    a:number = 0
    b?:number
}
let c = new C()
c.a=undefined // 报错
c.b=undefined
c.b=null  // 报错

非空断言操作符,加!可以推断为null

// sn为null就返回'default', 但是编译器不能区分null或undefined,加!可以推断为null
function f(sn:string | null):string{
    return sn! || 'default' 
}

// 编译器没法识别嵌套函数的null,加!推断不为null
function broken(name: string|null): string{
    function postfix(epithe: string){
        return name!.charAt(0)+'. the' + epithe
    }
    name = name || 'xiaoming'
    return postfix(name)
}
broken(null)

字面量类型(是js本身提供的一个变量)

type 用来约束取值只能是某几个字符串中的一个

  • 字符串字面量(将字符串作为一个类型来使用)
  • boolean、number字面量
type Easing = 'ease-in'|'ease-out'|'ease-in-out'
class UIElement {
    animate(dx:number, dy:number, easing:Easing){
        if(easing === 'ease-in'){
            // ...
        }else if(easing === 'ease-out'){

        }else if(easing === 'ease-in-out'){

        }
    }
}

let button = new UIElement()
button.animate(0,0,'ease-in')
button.animate(0,0,'uneasy') // 报错
type OnetoFive = 1|2|3|4|5
type Bools = true | false

映射类型

[K in T]: Type 类型变量 K 会把字符串字面量联合类型 T 的每个字符串都映射为属性
将一个已知的类型每个属性都变为可选、只读、promise等

// 将每个属性变为可选
interface PersonPartial {
    name?: string;
    age?: number;
}
// 简写
type aPartial<T> = {
    [P in keyof T]?: T[P]
}
type Person = {
    name: string
    age: number
}
type PersonPartial = aPartial<Person>


// 将每个属性变为只读
interface PersonReadonly {
    readonly name: string;
    readonly age: number;
}
// 简写
type aReadonly<T> = {
    readonly [P in keyof T]: T[P];
}
type Person = {
    name: string;
    age: number;
}
type PersonPartial = aReadonly<Person>;

它的语法与索引签名的语法类型,内部使用了 for … in。 具有三个部分:

  1. 类型变量 K,它会依次绑定到每个属性
  2. 字符串字面量联合的 Keys,它包含了要迭代的属性名的集合
  3. 属性的结果类型
type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };

条件类型

T extends U ? X : Y: 如果 T 可以赋值给 U,那么类型是 X,否则为 Y

type TypeName<T> = T extends string ? "string" : "number";

type Foo = TypeName<'ts'>; // type Foo = "string"
type Bar = TypeName<number>; // type Bar = "number"

分布式条件类型

type Baz = TypeName<1 | false> // 自动解析为 type Baz = "number" | "boolean"

类型转移 typeof

typeof用来捕获变量、类成员的类型(做类型约束和类型保护)
keyof用来捕获一个类型的键名称

const foo = '123456' // 捕获字符串的类型与值
let bar: typeof foo // 使用捕获到的类型
bar = '123456'
bar = 'sssss' // 报错,只能被赋值为123456,因为是常量
const colors={
    red:'red11',
    blue:'blue22'
}
type Colors = keyof typeof colors
let color:Colors
color='red'
color='red11' // 报错,捕获的是key名称

内置对象

1、ECMScript的内置对象
Boolean、Number、String、Date、RegExp、Error
2、BOM和BOM的内置对象
Window、Document、HTMLElement、DocumentFragment、Event、NodeList

// 变量类型是基本类型的布尔类型,但是赋值过来的是对象
let b:boolean = new Boolean(2)   // 报错
const  div:HTMLElement = document.getElementById('test')
const  divs:NodeListOf<HTMLElement> = document.querySelectorAll('div')
document.addEventListener('click',(event:MouseEvent)=>{...})   // Event是父类型 , MouseEvent是子类型
const fragment:DocumentFragment = document.createDocumentFragment()

模块(跟es6的一样)

把公共的功能单独抽离成一个文件作为一个模块。
模块里面的变量、函数、类都是私有的,如果想在外部访问模块里面的数据,要通过export将数据暴露出去,再通过import引入使用

// 导出
export function getData():any[]{
    return [{title:'111'},{title:'222'}]
}

// 引入
import { getData } from './modules/db'

命名空间

  • 解决:过多的全局变量会让代码变得不可维护,避免各种变量名相互冲突

  • ts的命名空间将代码包裹起来(即把相似功能的函数、类、接口放置到命名空间内),只对外部暴露需要在外部访问的对象

  • 编译后是一个匿名自执行函数,定义一个Home全局变量传进来,并把Page挂载到Home上去

  • 好处:可以使用类似模块化开发的方式,尽少生成全局变量,或者说把一组相关内容封装到一起去,对外提供统一的接口

命名空间和模块的区别?
命名空间:内部模块,主要用于组织代码,避免命名冲突
模块:ts外部模块的简称,侧重代码的复用,一个模块里可以有多命名空间

// page.ts
namespace Home{
	class Header{
		constuctor(){
			const elem=document.createElement('div')
			elem.innerText='this is Header'
			document.bdy.appendChild(elem)
		}
	}
	class Content{
		constuctor(){
			const elem=document.createElement('div')
			elem.innerText='this is Content'
			document.bdy.appendChild(elem)
		}
	}
	class Footer{
		constuctor(){
			const elem=document.createElement('div')
			elem.innerText='this is footer'
			document.bdy.appendChild(elem)
		}
	}
	export class Page{
		constuctor(){
			new Header()
			new Content()
			new Footer()
		}
	}
}

// 打包:编译后是一个匿名自执行函数,定义一个Home全局变量传进来,并把Page挂载到Home上去
var Home
(function(Home){
	var Header=(function(){
		function Header(){} return Header
	}())
	var Content=(function(){
		function Content(){} return Content
	}())
	var Footer=(function(){
		function Footer(){} return Footer
	}())
	var Page=(function(){
		function Page(){
			new Header()
			new Content()
			new Footer()
		} 
		return Page
	}())
	Home.Page = Page // 把Page挂载到Home上
})(Home||(Home={}))

// 使用
<script src='./dist/page.js'></script>
new Home.Page()

命名空间相互依赖:

// components.ts
namespace Componnets{
	// 还可以导出子命名空间
	export namespace SubComponents {
		export class Test {}
	}
	export interface User{name: string}
	export class Header{constructor(){}}
	export class Content{constructor(){}}
	export class Footer{constructor(){}}
}

// page.ts
// Home的命名空间依赖于components的
///<reference path='./components.ts' />
namespace Home{
	export class Page{
		user: Componnets.User = { name:'xiaoming' }
		constructor(){
			new Components.Header();
			new Components.Content();
			new Components.Footer();
		}
	}
}
// 属于A里面命名空间的代码
export namespace A{     // 也可以把命名空间暴露出去
    interface Animal{
        name:string
        eat():void
    }
    // 也需要暴露出去,外部才能用到
    export class Dog implements Animal{
        name:string
        constructor(name:string){
            this.name = name
        }
        eat(){
            console.log(`${this.name}`)
        }
    }
}


// 有了命名空间,B里面的接口名称和类名称完全可以跟A里面的一致
export namespace B{   // 也可以把命名空间暴露出去
    interface Animal{
        name:string
        eat():void
    }
    export class Dog implements Animal{...}
}
const ad=new A.Dog('小狗')
const bd=new B.Dog('大狗')
ad.eat()

// 引入
import { A, B } from './modules/a'
// 同一个页面 import这样使用
namespace Home{
    export class Foo {}
} 
import Bar = Home.Foo ;
let bar:Bar;

如何通过import对模块进行拆分和组合

  • 如何配置项输出amd
// componnets.ts
export class Header{}
export class Content{}
export class Footer{}


// page.ts
import {Header, Content, Footer} from './componnets'
export default class Page{
	constructor(){
		new Header()
		new Content()
		new Footer()
	}
}

// 编译完
define('components', ['require', 'exports'], function(require, export){
	var Header=(function(){
		function Header(){}
		return Header
	}())
	exports.Header = Header
	...
})
define('page', ['require', 'exports'], function(require, export){
	var Page = (function(){
		function Page(){
			new components_1.Header()
			...
		}
		return Page
	}())
	exports.default = Page
})
// 注意,要引入require.js
<script src="https://cdn.bootcdn.net/ajax/libs/require.js/0.10.0/require.js"></script>
<script src='./dist/page.js'>
require(['page'], function(page){
	new page.default()
})

装饰器

是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为
通俗讲就是,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能
常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)

和中间件的区别?中间件是利用装饰器原理实现的

tsconfig.jso的"experimentalDecorators": true, “emitDecoratorMetadata”: true

// 装饰器语法会提示错误
{ "compilerOptions": { "experimentalDecorators": true } }
  • 对类做修饰,不是对实例做修饰,所以类一定义,装饰器就去执行
  • 多个装饰器,执行顺序:从上到下
  • 可以函数里面再return一个函数解决传参问题
//  1. 类装饰器:在类声明之前被声明(紧接着类声明),用于类构造函数、用来监视,修改或替换类定义
//  1. 1 普通装饰器 
// 这种写法无法传参
function logClass(constructor:any){ // 参数是构造函数
    // 在不修改类的前提下,扩展类的属性和方法(截取东西做修改)
    constructor.prototype.apiUrl='xxx'
    constructor.ptototype.run=function(){...}
}

// 调用装饰器
@logClass   
class HttpClient{
    constructor(){}
    getData(){}
}
const http = new HttpClient()
// 测试
http.apiUrl


//  1.2 装饰器工厂(可传参的装饰器)
function logClass(params:string){ 
    return function(target:any){ // 接收类传过来的参数
        console.log(params) // 装饰器接收到的参数 'http://www.baidu.com/api'
        console.log(target) // 当前类
        target[attr] = params
    }
}

@logClass('http://www.baidu.com/api')  // 把参数赋给了params,把类赋给target
class HttpClient{
    constructor(){}
    getData(){}
}
const http = new HttpClient()
// 测试
http.apiUrl   // "http://www.baidu.com/api"



/* 2. 方法装饰器:(被用来做方法的)
	它被应用到方法的属性描述符上,可以用来监视,修改或替换方法定义
	方法装饰会在运行时传入下列3个参数:
	1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
	2、成员的名字
	3、成员的属性描述符
*/
function get(params:any){
    return function(target:any, methodName:any, desc:any){ // 当前类,方法名称,方法描述
        console.log(target) // HttpClient这个类
        console.log(methodName) //getData (劫持所有方法,处理事情)
        console.log(desc) 
        target.apiUrl='xxx'
        // 1.a保存当前方法
        var oMethod = desc.value
        desc.value=function(...args:any[]){  // 把getData方法替换了
            args = args.map(value=>{
                return String(value)
            })
            console.log(args)
            // 如果想修改而不是替换它,可以用对象冒充来实现
            oMethod.apply(this, ...args) // 把参数传进去
        }
    }
}
class HttpClient{
    constructor(){}
    @get('http://www.baidu.com/api')
    getData(){ console.log('我是getData里面的方法') }
}
const http = new HttpClient()


/**
3. 方法参数装饰器
参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素,传入下列3个参数:
1、对于静态成员来说是类的构造函数,对于实现成员是原型是类的对象
2、方法的名字
3、参数在函数列表中的索引
*/
function get(params:any){
    return function(target:any, methodName:any, desc:any){ // 当前类,方法名称,方法描述(对方法做编辑)
        console.log(target) // HttpClient这个类
        console.log(methodName) //getData (劫持所有方法,处理事情)
        console.log(desc) 
        target.apiUrl='xxx'
        // 1.a保存当前方法
        var oMethod = desc.value
        desc.value=function(...args:any[]){  // 把getData方法替换了
            args = args.map(value=>{
                return String(value)
            })
            console.log(args)
            // 如果想修改而不是替换它,可以用对象冒充来实现
            oMethod.apply(this, ...args) // 把参数传进去
        }
    }
}

// 在调用方法的时候,会执行装饰器,
class HttpClient{
    constructor(){}
    @get('http://www.baidu.com/api')
    getData(){ 
        console.log('我是getData里面的方法') 
    }
}
const http = new HttpClient()
http.getData() // 首先执行装饰器、再去getData


function get(params:any){
    return function(target:any, mparamsdName:any, paramId:any){ // 当前类,方法名称,方法描述
        console.log(target) // HttpClient这个类
        console.log(mparamsdName) //getData (劫持所有方法,处理事情)
        console.log(desc) 
        target.apiUrl='xxx'
    }
}

// 在调用方法的时候,会执行装饰器,
class HttpClient{
    constructor(){}
    @get('http://www.baidu.com/api')
    getData(@get('uuid') uuid:any){ 
        console.log('我是getData里面的方法') 
    }
}
const http = new HttpClient()
http.getData('123456')  // 首先执行装饰器、再去getData

执行顺序

@logClass('http://www.baidu.com/api')
@logClass('xxx') // 从下到上执行
class HttpClient{
    @logAttribue()
    constructor(){}
    @logMethod() // 从下到上执行
    getData(){
        return true
    }
    setData(@logParam1() attr1:any, @logParam2() attr2:any){}  // 从右到左执行
}

/* 执行顺序:
属性装饰器
方法装饰器
方法参数装饰器2
方法参数装饰器1
类装饰器2
类装饰器1
*/
// 访问器装饰器:在get上用了就不能在set上用,只能用一个
function visitDecorator(target:any,key:string,descriptor:PropertyDescriptor){
	// descriptor.writable = false
}

class Test{
	private _name: string
	constructor(name: string){
		this._name = name
	}
	get name(){
		return this._name
	}
	@visitDecorator
	set name(name:string){
		this._name =name
	}
}

const test=new Test('xiaoming')
test.name='hhhhh'
console.log(test.name)
// 属性装饰器:
function nameDecorator(target:any,key:string):any{
	// 创建了一个descriptor,用这个descriptor替换掉name属性的descriptor
	const descriptor:PropertyDescriptor = {
		writable: false
	}
	return descriptor
}

class Test
	@nameDecorator
	name = 'xiaoming'
}

const test=new Test()
test.name='hhhhh'
console.log(test.name)

ts的错误解读

  • 简洁的错误信息:错误号及错误信息常规的描述,不会告诉你这个错误为什么会发生,没有更深层次的信息
  • 详细的错误信息:引导用户了解错误发生的原因,比如告诉原因是类型不兼容

常规错误:

  • TS2304 cannot find name $``cannot find module jquery
    引入第三方库时没有声明所使用的任何内容

  • TS1148 cannot compile modules unless the '--module' flag provided

  • 捕获错误时,ts保护你免受js代码错误影响,所以要使用类型保护

try{
} catch (e){
	if(e instanceof Error){} // 要加这段
}
  • 接口ElementClass 不能同时扩展两个component类型
    在编译上下文中同时含有两个react.d.ts(@types/react/index.d.ts)
    可以删除node_modules和任何package-lock,然后再一次npm install
    如果不起作用,就去查找无效的模块,并将其报告给相关项目,你所使用的所有模块都应该将 react.d.ts 作为peerDependency,而不是 dependency 来使用

使用parcel打包ts

npm i parcel@next -D

// package.json
// parcel会帮助我们分析index.html里面的代码,自动把ts文件进行编译,编译成浏览器可以运行的代码,会起一个本地服务器,把编译的代码返回给我们
"scripts":{
	"start": "parcel ./src/index.html" 
}

// src/index.html
<script src='./page.ts' />

// src/page.ts
const name:string='xiaoming'
console.log(name)

声明/描述文件

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

  • 声明语句:如果需要ts对语法进行检查,需要加载对应的类型说明代码
    declare var jquery:(selector:string)=>any 说明定义了函数,返回any类型
  • 声明文件:把声明语句放到单独的文件(jquery.d.ts),ts会自动解析到项目中所有声明文件
  • 下载声明文件:npm i @types/jquery -S

其声明文件有两种写法:

  • 模块导出声明
export declare interface Message {
  success(content: MessageOptions): MessageType;
  error(content: MessageOptions): MessageType;
  loading(content: MessageOptions): MessageType;
  open: (config: MessageOptions) => MessageType;
  config: (options: MessageConfigOptions) => void;
  destroy: () => void;
}

export declare class Alert extends ZarmVueComponent {}
export interface haAlert {
  (message?: AlertOptions | AlertMessage, options?: AlertOptions) : Alert
}
  • 全局类型声明
declare module "abc" {
    interface funcAbcSign {
        (s: string): string
    }
    export let abc: funcAbcSign;
}

declare module '*.vue' {
  const Comp: any
  export default Comp
}

自定义jqurey声明文件

"scripts":{
	"start": "parcel ./src/index.html" 
}

// src/index.html
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.10.0/jquery.js"></script>
<script src='./page.ts' />

// src/page.ts
$(function(){ // 全局函数
	$('body').html('<div>123</div>')
	new $.fn.init()  // 构造函数init()
})
  • 全局类型定义
// jquery.d.ts
// 定义全局变量
declare var $:(param:()=>void)=>void

// 定义全局函数
interface JqueryInstance{
	html:(html:string) => JqueryInstance
}

// 接收的参数也是一个函数
declare function $(readyFunc:()=>void): void
// 函数重载:允许既是函数,又是对象
declare function $(selector:string): JqueryInstance
// 对对象进行类型定义,以及对类进行类型定义
declare namespace ${
	namespace fn{
		class init{}
	}
}

/*
	// 使用接口语法,实现函数重载
	interface JQuery{
		(readyFunc:()=>void): void
		(selector:string): JqueryInstance
	}
*/
declare var $: JQuery
  • 模块化的类型描述文件
// src/page.ts
import $ from 'jquery'
$(function(){ // 全局函数
	$('body').html('<div>123</div>')
	new $.fn.init()  // 构造函数init()
})
declare module 'jquery'{
	interface JqueryInstance{
		html:(html:string) => JqueryInstance
	}
	// 混合类型
	function $(readyFunc:()=>void): void
	function $(selector:string): JqueryInstance
	namespace ${
		namespace fn{
			class init{}
		}
	}
	export = $
}

webpack+ts

github代码

vue+ts

增加声明文件和ts相关loader

// tsconfig.json
{
  "compilerOptions": {
	"target": "es5", 
    "module": "commonjs", 
    "strict": true, 
    "esModuleInterop": true, 
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

// ts不能识别.vue文件,需要增加声明文件
// src/vue-shims.d.ts
declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}
// 安装
npm i vue vue-loader vue-template-compiler css-loader -D

//  webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
  entry: './src/index.ts',
  output: {
    filename: 'app.js',
  },
  resolve: {
    extensions: ['.js', '.ts', '.tsx', '.vue'],
    alias:{
      vue:'vue/dist/vue.esm.js' // es module不需要编译
    }
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      { 
      // 需要安装babel-loader、@babel/preset-env @babel/preset-typescript @babel/core -D
        test: /\.tsx?$/, 
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            '@babel/preset-env',
            '@babel/preset-typescript'
          ]
        }
        // use: [  // 这种也可以,只需要按照ts-loader
        //   {
        //     loader: 'ts-loader',
        //     options: { appendTsxSuffixTo: [/\.vue$/] }
        //   }
        // ]
      },
      {
        test:/\.css/,
        use:[
          'vue-style-loader',
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({  // 首页模版 并自动潜入输出文件
      template: './src/tpl/index.html'
    }),
    new VueLoaderPlugin()
  ]
}

// src/index.ts
import Vue from 'vue'
import Hello from './components/Hello.vue'
let app1= new Vue({
    el:'.app',
    data:{
        name:'Typescript'
    },
    template:`<Hello/><h1>{{name}}</h1>`,
    components: {Hello}
})

// package.json
"start": "webpack-dev-server --mode=development --config ./webpack.config.js",
"build": "webpack  --mode=production --config ./webpack.config.js",

react+ts

待整理…

jest+ts

npm install -D jest ts-jest @types/jest
// package.json
{
    // ...
    "scripts": {
        "build": "tsc",
        "test": "jest",
        "test-c": "jest --coverage"
    },
    "jest": {
        "testEnvironment": "node"
    },
    // ...
}

// jest.config.js
module.exports = {
    roots: [
        "<rootDir>/test"
    ],
    testRegex: 'test/(.+)\\.test\\.(jsx?|tsx?)$',
    transform: {
        "^.+\\.tsx?$": "ts-jest"
    },
    moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值