文章目录
简介
介绍
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)
泛型变量、类型、接口
- 泛型变量
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'}
- 泛型类型
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
- 泛型接口
// 把上面的对象字面量拿出来,改造成泛型接口
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
}
类型保护
联合类型出现的问题,才需要做类型保护
- 类型断言 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()
}
}
- in 检查一个对象上是否存在某个属性
function traninAnial(animal: Bird|Dog){
if('sing' in animal){
animal.sing()
}else{
animal.bark()
}
}
- typeof用来捕获变量、类成员的类型(做类型约束和类型保护)
function add(first:string|numbr, second:string|number){
if(typeof first === 'stirng' || typeof second === 'string'){
return `${first}${second}`
}
return first + second
}
- 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
}
- type 字面量做类型保护
可以使用===、==、!==、!=
来区分字面量类型
type state = 'yes'|'no'|'unknown'
function logOut(){
if(state == 'yes'){ ...
}else{...}
}
- strictNullChecks编译属性下的null和undefined
strictNullChecks:不允许把null、undefined 赋值给其他类型变量
可以使用==null 和 !==null来区分null和undefined
function foo(a?:number | null){
if(a == null) return
// a现在是number类型的
}
- 使用自定义的类型保护
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()
}
- 类型保护和回调函数
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。 具有三个部分:
- 类型变量 K,它会依次绑定到每个属性
- 字符串字面量联合的 Keys,它包含了要迭代的属性名的集合
- 属性的结果类型
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
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'],
};