TS入门

431 阅读9分钟

目录

一、快速上手

二、vue的TS

三、使用vue-class-component

四、vue-property-decorator

五、TypeScript 不适合在 vue 业务开发中使用吗?

六、Property 'name' has no initializer

七、下载量对比

八、tsconfig.json

九、shims-vue.d.ts

十、shims-tsx.d.ts

十一、vuex-class

十二、ts

十三、装饰器(Decorator)

十四、红绿灯

十五、Property '$router' does not exist on type 'Login'

十六、使用createDecorator自定义装饰器

十七、TSLint去掉分号和逗号的检查

十八、react与TS

十九、图片转base64

二十、React+TS

二十一、外部声明和三斜线指令

二十二、TS+Webpack

二十三、yarn清缓存和卸载全局安装的包

二十四、修改脚手架使用npm下载还是使用yarn下载

二十五、倒计时

参考链接:

一、快速上手

TypeScript 是 JavaScript 的强类型版本。然后在编译期去掉类型和特有语法,生成纯粹的 JavaScript 代码。

TypeScript 是 JavaScript 的超集,这意味着他支持所有的 JavaScript 语法。

强类型语言的优势在于静态类型检查。

TypeScript是微软开发的语言。

vue3.0使用ts开发。

是github开源项目:github.com/Microsoft/T…

2012 年 10 月诞生。

vscode是用ts编写的:github.com/Microsoft/v…

装包:

yarn global add typescript

检查版本:

tsc -V

初始化:

tsc --init

index.ts:

function greeter(person) { return "Hello, " + person; }

let user = "Jane User";

document.body.innerHTML = greeter(user);

编程成js文件:

tsc index.ts

index.js:

function greeter(person) { return "Hello, " + person; } var user = "Jane User"; document.body.innerHTML = greeter(user);

类型检查:

如果函数参数声明是字符串,却传了数字,会有警告信息

function greeter(person:string) { return "Hello, " + person; }

let user = 1;

document.body.innerHTML = greeter(user);

及时不传参数也会报错:

interface接口:

定义Person包含的字段

interface Person { firstName: string; lastName: string; }

function greeter(person: Person) { return "Hello, " + person.firstName + " " + person.lastName; }

let user = { firstName: "Jane", lastName: "User" };

document.body.innerHTML = greeter(user);

使用class创建类:

class Student { fullName: string; constructor(public firstName, public middleInitial, public lastName) { this.fullName = firstName + " " + middleInitial + " " + lastName; } }

interface Person { firstName: string; lastName: string; }

function greeter(person : Person) { return "Hello, " + person.firstName + " " + person.lastName; }

let user = new Student("Jane", "M.", "User");

document.body.innerHTML = greeter(user);

编译后是:

var Student = /** @class */ (function () { function Student(firstName, middleInitial, lastName) { this.firstName = firstName; this.middleInitial = middleInitial; this.lastName = lastName; this.fullName = firstName + " " + middleInitial + " " + lastName; } return Student; }()); function greeter(person) { return "Hello, " + person.firstName + " " + person.lastName; } var user = new Student("Jane", "M.", "User"); document.body.innerHTML = greeter(user);

访问网页,index.html:

Document

效果:

自动补全功能:

二、vue的TS

引入Vue文件的时候需要加上.vue后缀,否则编辑器识别不到

TS路由:

import Vue from 'vue' import VueRouter, { RouteConfig } from 'vue-router' import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes: Array = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') } ]

const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes })

export default router

import Vue from 'vue' import VueRouter, { RouteConfig } from 'vue-router'

Vue.use(VueRouter)

const routes: Array = [ { path: '/', redirect: '/login' }, { path: '/login', component: () => import('../views/Login.vue') } ]

const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes })

export default router

三、使用vue-class-component

class-component.vuejs.org/

github.com/vuejs/vue-c…

annotating:注释

加加减减:

或者:

v-model:

挂载完生命周期:

计算属性:

父子组件传值:

父组件

子组件

子组件也可以写成这种

增加路由hook:

vue-class-component.ts

import Component from 'vue-class-component'

Component.registerHooks([ "beforeRouteEnter" ])

在入口文件main.ts里引入这个文件

在组件里可以使用

四、vue-property-decorator

www.npmjs.com/package/vue…

vue属性装饰器

父子组件传值,传参

父组件:

子组件:

五、TypeScript 不适合在 vue 业务开发中使用吗?

www.zhihu.com/question/31…

六、Property 'name' has no initializer

Property 'name' has no initializer and is not definitely assigned in the constructor.

解决办法一:

把tsconfig.json文件里的strict字段改成false

解决办法二:

在属性名后面加叹号,这是一种修饰符,标识忽略

七、下载量对比

八、tsconfig.json

ts的配置

{ "compilerOptions": { "target": "esnext", //编译的目标版本 "module": "esnext", //指定生成哪个模块系统代码 "strict": true, //静态类型检查 "jsx": "preserve", // "importHelpers": true, "moduleResolution": "node", "experimentalDecorators": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "sourceMap": true, // 是否生成map文件 "baseUrl": ".", "types": [ "webpack-env" ], "paths": { "@/": [ "src/" ] }, "lib": [ "esnext", "dom", "dom.iterable", "scripthost" ] }, "include": [ "src//*.ts", "src//.tsx", "src/**/.vue", "tests//*.ts", "tests//*.tsx" ], "exclude": [ "node_modules" ] }

九、shims-vue.d.ts

shims: 垫片

由于 TypeScript 默认并不支持 .vue 后缀的文件,所以在 vue 项目中引入的时候需要创建一个shims-vue.d.ts 文件,放在项目应使用目录下,例如 src/shims-vue.d.ts,用来支持.vue 后缀的文件;

主要用于 TypeScript 识别.vue 文件,Ts默认并不支持导入 vue 文件,这个文件告诉ts 导入.vue 文件都按VueConstructor处理

declare module '*.vue' { import Vue from 'vue' export default Vue }

十、shims-tsx.d.ts

允许你以.tsx结尾的文件,在Vue项目中编写jsx代码

import Vue, { VNode } from 'vue'

declare global { namespace JSX { // tslint:disable no-empty-interface interface Element extends VNode {} // tslint:disable no-empty-interface interface ElementClass extends Vue {} interface IntrinsicElements { [elem: string]: any; } } }

十一、vuex-class

www.npmjs.com/package/vue…

使用仓库做加加减减

仓库:

import Vue from 'vue' import Vuex from 'vuex'

Vue.use(Vuex)

interface Payload { key: string, value: any }

export default new Vuex.Store({ state: { count: 0 }, mutations: { setState(state:any, payload:Payload) { state[payload.key] = payload.value } }, actions: { }, modules: { } })

页面:

十二、ts

初始化:

在空文件夹里打开终端 -> 输入tsc --init 自动生成tsconfig.json

{ "compilerOptions": { /* Basic Options / // "incremental": true, / Enable incremental compilation / "target": "es5", / Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. / "module": "commonjs", / Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. / // "lib": [], / Specify library files to be included in the compilation. / "allowJs": true, / Allow javascript files to be compiled. / // "checkJs": true, / Report errors in .js files. / // "jsx": "preserve", / Specify JSX code generation: 'preserve', 'react-native', or 'react'. / // "declaration": true, / Generates corresponding '.d.ts' file. / // "declarationMap": true, / Generates a sourcemap for each corresponding '.d.ts' file. / // "sourceMap": true, / Generates corresponding '.map' file. / // "outFile": "./", / Concatenate and emit output to single file. / "outDir": "./js", //输出文件夹 / Redirect output structure to the directory. / // "rootDir": "./", / Specify the root directory of input files. Use to control the output directory structure with --outDir. / // "composite": true, / Enable project compilation / // "tsBuildInfoFile": "./", / Specify file to store incremental compilation information / // "removeComments": true, / Do not emit comments to output. / // "noEmit": true, / Do not emit outputs. / // "importHelpers": true, / Import emit helpers from 'tslib'. / // "downlevelIteration": true, / Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. / // "isolatedModules": true, / Transpile each file as a separate module (similar to 'ts.transpileModule'). */

/* Strict Type-Checking Options */
"strict": true,                           /* Enable all strict type-checking options. */
// "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true,              /* Enable strict null checks. */
// "strictFunctionTypes": true,           /* Enable strict checking of function types. */
// "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */

/* Additional Checks */
// "noUnusedLocals": true,                /* Report errors on unused locals. */
// "noUnusedParameters": true,            /* Report errors on unused parameters. */
// "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */

/* Module Resolution Options */
// "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
// "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [],                       /* List of folders to include type definitions from. */
// "types": [],                           /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */

/* Source Map Options */
// "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

/* Experimental Options */
"experimentalDecorators": true,   //装饰器      /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */

/* Advanced Options */
"forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */

} }

监视ts文件变化,自动编译:

vscode -> 终端 -> 运行任务 -> typescript -> tsc: 监视 - (tsconfig.json的目录)

课堂练习(1):

function test(name:string) { console.log(name) }

test('hello3')

//元组 let tuple:[string, number, string] = ['a', 1, 'b'] console.log(tuple)

//任意值 let person:any = 'xu' console.log(person)

//四要素:调用, 参数,返回值,作用 function fun(name:string):string { console.log(name) return name }

fun('xu')

//never 不可到达 const error = ():never => { throw new Error('错误') }

//error()

const loop = () => { while(true); } //loop()

console.log(1)

//枚举 enum Color { Red = 2, Green, Blue }

console.log(Color.Red) //2

enum obj { None, Read, G = '123'.length }

console.log(obj)

enum Enum { A }

let a = Enum.A console.log(a) //0

let na = Enum[a] console.log(na) //A

课堂练习(2):

function sayHello(person: string) { return 'Hello,' + person }

let user:string = 'a' console.log(sayHello(user))

let num:number = 1

//没有显示声明类型,则会进行类型推断 let a = 'a' a = 'b' //赋值其他类型,则会报错

let flag:boolean = true

//数字类型的数组 let arr:number[] = [1,2] let strArr:string[] = ['a', 'b'] let objArr:object[] = [{}]

//泛型方式 let arr1:Array = [1] let strArr1:Array = ['a','b']

//元组 let arr2:[number, string, boolean] = [1, '2', true]

//null, undefined let my_null:null = null let my_undefined:undefined = undefined

//可以是字符串也可以是undefined let c:string | undefined console.log(c)

//never 从来不会出现的值 // let my_never: never = (() => { // throw new Error() // })()

//any 任何类型 let my_any:any = '任何类型' console.log(my_any)

//任何类型的数组 let arrAny:any[] = ['a', 1, true]

function run():void { console.log('run') }

run()

function getName():string { return 'xu' }

console.log(getName())

const myRun = ():void => { console.log('myRun') }

myRun()

const myGetName = ():string => { return 'myGetName' }

console.log(myGetName())

//构造函数Boolean 创造的不是布尔值 //let myBoolean:boolean = new Boolean(1)

课堂练习(3):

//函数 //没有返回值是用void function getInfo(name:string, age:number):string { return 我叫${name},今年${age}岁。 }

console.log(getInfo('xu', 30))

let getInfo1 = (name:string, age:number):string => { return 我叫${name},今年${age}岁。 }

console.log(getInfo('xu', 31))

//完整类型 let getInfo2:(name:string, age:number) => string = (name:string, age:number):string => { return 我叫${name},今年${age}岁。 }

let getInfo3:Function = (name:string, age:number):string => { return 我叫${name},今年${age}岁。 }

//函数可选参数,加个问好 function myInfo(name: string, age?: number):string { if (typeof age === 'number') { return 我叫${name},今年${age}岁。 } else { return 我叫${name} } }

console.log(myInfo('xu'))

//默认值 function myInfo1(name: string, age:number = 32):string { if (typeof age === 'number') { return 我叫${name},今年${age}岁。 } else { return 我加${name} } }

console.log(myInfo1('xu'))

//求和 function sum(a:number, b:number):number { return a + b }

console.log(sum(1, 2))

//剩余参数 function sum1(...rest:number[]):number { console.log(rest) //数组 return rest.reduce((prev, item) => { return prev + item }, 0) }

console.log(sum1(1, 2, 3, 4, 5))

//枚举类型 // 0: 女, 1:男, 2:保密

enum Sex { gril, boy, secret }

let xu:Sex = Sex.boy console.log(xu) let xu1:Sex = 3 console.log(xu1)

function getSex(sex:Sex):string { if (sex === Sex.gril) { return '我是女孩' } else if (sex === Sex.boy) { return '我是男孩' } else { return '保密' } }

console.log(getSex(xu))

class A { v:number = 100 test(num:number) { if (num > this.v) { console.log('大了'); } else if (num < this.v) { console.log('小了') } else { console.log('对了') } } }

let a = new A()

a.test(100)

let isDone:boolean = false

let isDone1:object = new Boolean(1)

let isDone2:Boolean = new Boolean(1)

let isDone3:boolean = Boolean(1)

//变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型 let something //let something:any something = 1 something = 'a'

//联合类型 let myA: string | number myA = 'a' myA = 1

function getLength(something: string | []):number { return something.length }

let myB: string | number myB = 'A' myB.length myB = 1 //myB.length //报错

let arr:number[] = [1, 3, 2] arr.sort((a, b) => { return a - b })

console.log(arr)

function is(ar:string,sr:string):boolean { let result = true if(ar.length===sr.length){ for(let i=0;i<ar.length;i++){ if(ar.indexOf(sr[i])===-1){ result = false } } result = true } else { result = false } return result } console.log(is("asa","aas") )

function isAng(arr:string,trr:string):boolean{ if(arr.length===trr.length){ for(var i=0;i<arr.length;i++){ if(arr.indexOf(trr[i])===-1){ return false } } return true } return false }

console.log(isAng('listen', 'silenta'))

课堂练习(4):

//函数重载 function add(a:string, b:string):string; function add(a:number, b:number):number;

function add(a:any, b:any): any { if (typeof a === 'string') { return a + '---' + b } else { return a + b } }

console.log(add('a', 'b'))

//类 //修饰符 pubulic protected private //静态属性 static class Person { public name: string protected age: number

static height: number = 170

constructor(name:string, age:number) { this.name = name this.age = age console.log(Person.height, Person.getHeight()) }

protected getName():string { return 我的名字叫${this.name} }

static getHeight():number { return this.height } }

let xu = new Person('xu', 30) //console.log(xu.getName()) console.log(Person.getHeight())

// class Animal { // name:string // constructor(name:string) { // this.name = name // } // sayHi() { // return My name is ${this.name} // } // }

// let a = new Animal('jack') // console.log(a.sayHi())

//继承 class Programmer extends Person { job:string constructor(name:string, age:number, job:string) { super(name, age) this.job = job console.log(this.age) } getJob() { return ${this.getName()},年龄${this.age},我的工作是${this.job} } }

let xu1 = new Programmer('徐同保', 30, 'web前端') //console.log(xu1.getName()) console.log(xu1.getJob()) //console.log(xu1.age)

//抽象类 abstract class Animal { name: string constructor(name: string) { this.name = name } getName():string { return this.name } abstract eat():void }

class Cat extends Animal { food: string constructor(name: string, food: string) { super(name) this.food = food } eat():void { console.log(${this.getName()}爱吃鱼) } }

let ketty = new Cat('小猫', '鱼') ketty.eat()

//更简练的写法,不用定义name了,也不需要赋值 class MyAnimal { constructor(public name:string) { } getName() { return this.name } }

let myAnimal = new MyAnimal('小猫') console.log(myAnimal.getName())

//接口 interface Goods { name: string price: number flag: boolean }

let cartList: Goods[] = [ { name: '苹果', price: 8, flag: true }, { name: '香蕉', price: 5, flag: false } ]

function goodsInfo(goods:Goods) { console.log(${goods.name}现在${goods.price}元一斤${goods.flag ? ',正在促销' : ''}) }

cartList.forEach(item => { goodsInfo(item) })

//函数接口 interface GoodsInfo { (goods: Goods): string }

let myGoodsInfo: GoodsInfo = (goods:Goods):string => { return ${goods.name}现在${goods.price}元一斤${goods.flag ? ',正在促销' : ''} }

//类接口,实现接口的时候使用implements(实现) interface PersonI { name: string, age: number, getName():string }

interface WebI { name: string, age: number, job: string getName(): string getJob(): string }

//接口也可以继承 interface WebIPlus extends PersonI { job: string, getJob(): string }

class MyPerson implements PersonI { name: string age: number

constructor(name:string, age:number) { this.name = name this.age = age }

getName() { return this.name } }

class Web extends MyPerson implements WebIPlus { job: string constructor(name: string, age: number, job: string) { super(name, age) this.job = job }

getJob() { return this.job }

getInfo() { return ${this.name},${this.age},${this.job} } }

let xu2 = new Web('xu', 30, 'web') console.log(xu2.getInfo())

课堂练习(5):

let arr:Array = ['a', 'b', 'c']

//泛型函数 function getMin(arr:T[]):T { let min:T = arr[0] arr.forEach(val => { if (val < min) { min = val } }) return min }

let min = getMin([5, 2, 8, 4, 4]) console.log(min)

let min1 = getMin(['d', 's', 'c', 'e', 'f']) console.log(min1)

//泛型类 class Min { list: T[] = [] add(params: T):void { this.list.push(params) } getMin(): T { let min:T = this.list[0] this.list.forEach(val => { if (val < min) { min = val } }) return min } }

//函数接口泛型 interface Min { (arr: T[]): T }

//装饰器 function logClass(target:any) { console.log('我是装饰器', target) target.prototype.name = '我是装饰器添加的name' target.prototype.getName = function () { console.log('我是装饰器添加的方法') } }

function logName(params:any) { console.log(params) return function(target:any, attr:any) { console.log('属性装饰器', target) //实例 console.log('attr', attr) //使用装饰器的属性 target[attr] = params } }

// function logName(target:any, attr:any) { // console.log('属性装饰器', target) //实例 // console.log('attr', attr) //使用装饰器的属性 // target[attr] = 'xu' // }

@logClass class Person { @logName('我是Person类') myName: string | undefined

@logName(40) age:number | undefined

getInfo() { console.log(this.myName, this.age) } }

let a = new Person() console.log(a) a.getInfo()

笔记:

TypeScript 中,使用 : 指定变量的类型,: 的前后有没有空格都可以。

TypeScript 只会进行静态检查,如果发现有错误,编译的时候就会报错。

构造函数 Boolean 创造的对象不是布尔值。

当构造函数修饰为 private 时,该类不允许被继承或者实例化。

当构造函数修饰为 protected 时,该类只允许被继承,不允许实例化。

只读属性关键字,只允许出现在属性声明或索引签名或构造函数中。

一个类只能继承自另一个类,但是可以实现多个接口。

变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型。

联合类型使用 | 分隔每个类型。

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。 十三、装饰器(Decorator)

提案官网:github.com/tc39/propos…

tc39.es/proposal-de…

装饰器是一个对类进行处理的函数。装饰器函数的第一个参数,就是所要装饰的目标类。

@decorator class A {}

// 等同于

class A {} A = decorator(A) || A;

如果觉得一个参数不够用,可以在装饰器外面再封装一层函数。

function testable(isTestable) { return function(target) { target.isTestable = isTestable; } }

mixins实例:

//装饰器函数,list合并到原型链上 const mixins = (...list:any) => (target:any) => { Object.assign(target.prototype, ...list) }

//待合并的对象 const Foo = { name: 'xu', foo() { console.log('foo') }, bar() { console.log('bar') } }

//使用装饰器 @mixins(Foo) class MyClass {

}

//测试 let obj = new MyClass(); (obj as any).foo(); (obj as any).bar(); console.log((obj as any).name)

装饰器有注释的作用。

由于存在函数提升,使得装饰器不能用于函数。

日志实例:

//计算器类 class Computed { name:string constructor(name:string) { this.name = name } @log add(a:number, b:number) { console.log(this.name) return a + b }

@log sub(a:number, b:number) { return a - b } }

//日志装饰器 function log(target:any, name:any, descriptor:any) { let oldValue = descriptor.value descriptor.value = function () { //日志 console.log(日志:调用${name},参数是${Array.from(arguments)}) //return oldValue.call(this, ...arguments) //执行老方法 return oldValue.apply(this, arguments) } return descriptor }

//测试 const computed = new Computed('xu') console.log(computed.add(2, 4)) console.log(computed.sub(4, 2))

十四、红绿灯

class Light { constructor() { this.init() } init() { return this.light(3, '绿灯') .then(() => { return this.light(2, '红灯') }) .then(() => { return this.light(1, '黄灯') }).then(() => { this.init() }).catch(() => { console.log('失败了') }) } light(time: number, type: string) { return new Promise((resolve, reject) => { let timer = setInterval(() => { console.log(type, time) time-- if (time === 0) { clearInterval(timer) resolve() //reject() } }, 1000) }) } }

let ligth = new Light()

interface LampI { loop: number start(): void }

//红绿灯 class Lamp { loop: number constructor(public green: number, public red: number, public yellow: number) { this.loop = 0 } start() { let loopFun = async () => { await new Promise((resolve) => { setTimeout(() => { this.loop++ this.print() loopFun() }, 1000) }) } loopFun() } test(a:T) { console.log(a) } private print(): void { if (this.loop <= this.green) { console.log(绿灯${this.green + 1 - this.loop}) } else if (this.loop <= this.green + this.red) { console.log(红灯${this.green + this.red + 1 - this.loop}) } else if (this.loop <= this.green + this.red + this.yellow) { console.log(黄灯${this.green + this.red + this.yellow + 1 - this.loop}) } else { this.loop = 1 console.log(绿灯${this.green}) } } }

let lamp = new Lamp(3, 2, 1) lamp.start() lamp.test([1, 2, 3]) lamp.test('hello')

十五、Property '$router' does not exist on type 'Login'

解决办法:

(1)在使用路由的组件导入路由包:

import VueRouter from 'vue-router';

(2)把this断言为any

(this as any).$router.push('/index')

(3)在类里定义$router属性

export default class Login extends Vue { $router }

十六、使用createDecorator自定义装饰器

decorators.ts:

import { createDecorator } from 'vue-class-component'

export const Log = createDecorator((options:any, key:string) => { // 备份原始方法 const originalMethod = options.methods[key]

// 添加日志逻辑 options.methods[key] = function wrapperMethod(...args:any) { // 打印日志 console.log(日志: ${key}(, ...args, ')')

// 执行原始方法
originalMethod.apply(this, args)

} })

export const LogPlus = (payload: string) => createDecorator((options:any, key:string) => { // 备份原始方法 const originalMethod = options.methods[key]

// 添加日志逻辑 options.methods[key] = function wrapperMethod(...args:any) { // 打印日志 console.log(${payload}日志: ${key}(, ...args, ')')

// 执行原始方法
originalMethod.apply(this, args)

} })

使用:

十七、TSLint去掉分号和逗号的检查

"trailing-comma": [false],
"semicolon": [false],

十八、react与TS

脚手架:

npx create-react-app react-ts-app --typescript

按装用到的包:

yarn add react-router-dom redux react-redux redux-thunk redux-logger axios

安装TS版的声明:

yarn add @types/react-router-dom @type/react-redux

十九、图片转base64

const getBase64Image = (url: string) => { return new Promise((resolve,reject) => { let image = new Image(); // CORS 策略,会存在跨域问题stackoverflow.com/questions/2… image.setAttribute("crossOrigin",'Anonymous'); image.src = url; image.onload = () => { let canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; (canvas as any).getContext('2d').drawImage(image,0,0); let result = canvas.toDataURL('image/png'); resolve(result); } image.onerror = () => { reject('图片流异常'); }; })

}

二十、React+TS

受控组件:

handleInput(e: ChangeEvent, field: string) { // @ts-ignore this.setState({ [field]: e.target.value }) }

HTMLInputElement:

keyUp事件:

handleEnter(e: KeyboardEvent) { if(e.keyCode === 13) { this.handleLogin() } }

点击事件:

handleVisible(e: MouseEvent, count: number) { let { visible } = this.state this.setState({ visible: !visible }) }

忽略类型检查:

// @ts-ignore

滚动事件:

const handleScroll = (e: React.UIEvent<HTMLDivElement, globalThis.UIEvent>) => { let scrollTop = (e.target as Element).scrollTop console.log(scrollTop) }

withRouter:

redux数据读写:

import React, { Dispatch } from 'react' import { connect } from 'react-redux' import { IState } from '../interface'

interface IProps { list: any[], currentId: number, onSetState: (key: string, value: any) => void onDispatch: (action: Function) => void }

const Sidebar = (props: IProps) => { const { list, currentId } = props

const handleNav = (id: number) => { props.onSetState('currentId', id); //@ts-ignore document.getElementById(id + '').scrollIntoView({ block: 'start', behavior: 'smooth' }) }

const sidebarDom = list.map((item: any) => ( <div key={item.id} className={m-sidebar-item ${currentId === item.id ? 'active' : ''}} onClick={() => handleNav(item.id) }>{item.title} ))

return (

{sidebarDom}
) }

const mapStateToProps = (state: IState) => { return { list: state.book.list, currentId: state.book.currentId } }

const mapDispatchToProps = (dispatch: Dispatch) => { return { onSetState(key: string, value: any) { dispatch({ type: 'SET_STATE', key, value }) }, onDispatch(action: Function) { dispatch(action) } } }

export default connect(mapStateToProps, mapDispatchToProps)(Sidebar)

reducer:

import { IBookState, IAction } from '../../interface'

const defaultState: IBookState = { title: '小米书城', currentId: 0, isRealScroll: true, list: [], myBooks: [] }

const reducer = (state = defaultState, action: IAction) => { switch (action.type) { case 'SET_STATE': let newState = JSON.parse(JSON.stringify(state)) newState[action.key] = action.value return newState default: return state } }

export default reducer

actionCreator.js:

import { Dispatch } from 'react' import Api from '../../api'

const list = () => (dispatch: Dispatch) => { Api.list().then(res => { if (res.code === 200) { dispatch({ type: 'SET_STATE', key: 'list', value: res.data }) } }) }

export default { list }

接口:

export interface IBookState { title: string, currentId: number, isRealScroll: boolean, list: any[], myBooks: any[] }

export interface IState { book: IBookState }

export interface IAction { type: string, key: string, value: any }

登录页hook写法:

import React, { useState, MouseEvent, KeyboardEvent } from 'react' import { RouteComponentProps } from 'react-router-dom' import Icon from '../components/Icon' import Api from '../api'

const Login = (props: RouteComponentProps) => { const [ username, setUsername ] = useState('admin') const [ password, setPassword ] = useState('123456') const [ visible, setVisible ] = useState(false)

const handleVisible = (e: MouseEvent, count: number ) => { setVisible(!visible) }

const handleEnter = (e: KeyboardEvent) => { if (e.keyCode === 13 ) { handleLogin() } }

const handleLogin = () => { Api.login({ username, password }).then(res => { if (res.code === 200) { localStorage.setItem('token', res.data.username) props.history.push('/index/home') } }) }

return (

<input value={username} onChange={(e) => setUsername(e.target.value)} placeholder="请输入用户名" autoFocus>
<input value={password} onChange={(e) => setPassword(e.target.value)} onKeyUp={ (e) => handleEnter(e) } placeholder="请输入密码" type={ visible ? 'text' : 'password' }> <Icon name={ visible ? 'show' : 'hide' } onClick={ (e, count) => handleVisible(e, count) } className="m-login-icon">
<button onClick={() => handleLogin()}>登录
) }

export default Login

登录页class写法:

import React, { Component, ChangeEvent, KeyboardEvent, MouseEvent } from 'react' import { RouteComponentProps } from 'react-router-dom' import Icon from '../components/Icon' import Api from '../api'

interface IState { username: string, password: string, visible: boolean }

interface IProp extends RouteComponentProps {

}

export default class LoginClass extends Component<IProp, IState> { constructor(props: IProp) { super(props) this.state = { username: 'admin', password: '123456', visible: false } }

handleInput(e: ChangeEvent, field: string) { // @ts-ignore this.setState({ [field]: e.target.value }) }

handleVisible(e: MouseEvent, count: number) { let { visible } = this.state this.setState({ visible: !visible }) }

handleEnter(e: KeyboardEvent) { if(e.keyCode === 13) { this.handleLogin() } }

handleLogin() { let { username, password } = this.state Api.login({ username, password }).then(res => { if (res.code === 200) { localStorage.setItem('token', res.data.username) this.props.history.push('/index/home') } }) }

render() { let { username, password, visible } = this.state return (

<input value={username} onChange={(e) => this.handleInput(e, 'username')} placeholder="请输入用户名" autoFocus />
<input value={password} onChange={(e) => this.handleInput(e, 'password')} onKeyUp={ (e) => this.handleEnter(e) } placeholder="请输入密码" type={ visible ? 'text' : 'password' }/> <Icon name={ visible ? 'show' : 'hide' } onClick={ (e, count) => this.handleVisible(e, count) } className="m-login-icon">
<button onClick={() => this.handleLogin()}>登录
) } }

Icon组件hook写法:

import React, { MouseEvent } from 'react'

interface IProps { name: string, className?: string, onClick?: (e: MouseEvent, count: number) => void }

const Icon = (props: IProps) => { let { name, className = '', onClick = () => {} } = props return ( <span className={icon iconfont icon-${name} ${className}} onClick={(e) => onClick(e, 1)}> ) }

export default Icon

Icon组件Class写法:

import React, { Component, MouseEvent } from 'react'

interface IProps { name: string, className?: string, onClick?: (e: MouseEvent, count: number) => void }

export default class IconClass extends Component { render() { const { name, className = '', onClick = () => {} } = this.props return ( <span className={icon iconfont icon-${name} ${className}} onClick={(e) => onClick(e, 1)}> ) } }

ReactElement:

import React, { Dispatch, ReactElement } from 'react' import Header from '../components/Header' import Footer from '../components/Footer' import { Switch, Route } from 'react-router-dom' import { connect } from 'react-redux' import Home from './Home' import MyBooks from './MyBooks' import Me from './Me'

interface IProps { onSetState: (key: string, value: any) => void onDispatch: (action: Function) => void }

const Index = (props: IProps) => {

const renderComponent = (Component: ReactElement, title: string) => { setTimeout(() => { props.onSetState('title', title) })

return Component

}

return (

<Route path="/index/home" render={() => renderComponent(, '小米书城')}> <Route path="/index/my_books" render={() => renderComponent(, '我的书包')}> <Route path="/index/me" render={() => renderComponent(, '个人中心')}>
) }

const mapStateToProps = () => { return { } }

const mapDispatchToProps = (dispatch: Dispatch) => { return { onSetState(key: string, value: any) { dispatch({ type: 'SET_STATE', key, value }) }, onDispatch(action: Function) { dispatch(action) } } }

export default connect(mapStateToProps, mapDispatchToProps)(Index)

二十一、外部声明和三斜线指令

declare:声明

reference:引用

env:environment 环境

namespace:命名空间

module: 模块

外部声明一般针对第三方来历不明的库,当你想要在你的typescript项目中使用用javascript代码写的第三方库时,就需要用到外部声明。一个常见的例子,假设我们在HTML中通过script标签引入了全局jQuery:

// 注册全局变量 $

path/to/jquery.js文件在会在全局作用域中引入对象,接下来如果在同一项目下的TypeScript文件中使用`,接下来如果在同一项目下的TypeScript文件中使用 ``,TypeScript编译器会报错:

// 错误,缺少名字 的声明信息//errorTS2581:Cannotfindname 的声明信息 // error TS2581: Cannot find name ''. Do you need to install type definitions for jQuery? Try npm i @types/jquery $('body').html('hello world');

由于没有任何类型信息,TypeScript编译器根本不知道 $ 代表的是什么,此时需要引入外部声明(因为$是外部JavaScript引入TypeScript代码中的)。外部声明的关键字是:

分析语句 $('body').html('hello world'); 得出:

- `$`是一个函数,接收字符串参数
- `$`调用返回值是一个对象,此对象拥有成员函数 `html`,这个成员函数的参数也是字符串类型

// 声明 的类型信息declarelet的类型信息 declare let: (selector: string) => { html: (content: string) => void; };

// 正确,已经通过外部声明已经通过外部声明 ('body').html('hello world');

声明应该是纯粹对于一个标识符类型或外观的描述,便于编译器识别,外部声明具有以下特点:

- 必须使用 `declare` 修饰外部声明
- 不能包含实现或初始化信息(内部声明可以在声明的时候包含实现或初始化)

// 声明a为一个数字 declare let a: number; // 错误,外部声明不能初始化 // error TS1039: Initializers are not allowed in ambient contexts declare let b: number = 2;

// 声明T为一个接口 declare interface T {} // 声明接口类型变量b let b: T;

// 声明fn为一个函数 // 错误,声明包含了函数实现 // error TS1183: An implementation cannot be declared in ambient contexts declare function fn(){}

// 正确,不包含函数体实现 declare function fn(): void;

// 声明myFunc为一个函数 declare let myFunc: (a: number) => void;

// 声明MyEnum枚举类型 declare enum MyEnum { A, B }

// 声明NS为命名空间 declare namespace NS { // 错误,声明不能初始化 // error TS1039: Initializers are not allowed in ambient contexts const a: number = 1; // 正确,仅包含声明 const b: number; // 正确,函数未包含函数体实现 function c(): void; }

// 声明一个类 declare class Greeter { constructor(greeting: string); greeting: string; showGreeting(): void; }

外部声明还可以用于声明一个模块,如果一个外部模块的成员要被外部访问,模块成员应该用 export 声明导出:

declare module 'io' { export function read(file: string): string; export function write(file: string, data: string): void; }

习惯上,常常把外部声明写在一个后缀名为 .d.ts 的声明文件中,然后用三斜线指令引入进来

// jquery.d.ts 文件 declare let $: (selector: string) => { html: (content: string) => void; };

// main.ts 文件 /// $('body').html('hello world');

上述语句声明了 main.ts 依赖 jquery.d.ts 声明文件,在编译阶段,被依赖文件 jquery.d.ts 将被包含进来,就像将被依赖文件的源码展开在依赖声明处一样:

// main.ts文件等价于将代码在三斜线指令处展开 declare let :(selector:string)=>html:(content:string)=>void;;: (selector: string) => { html: (content: string) => void; }; ('body').html('hello world');

三斜线指令中需要注意的是 path 类型和 types 类型的区别:

/// ///

- `path` 类型声明的是对本地文件的依赖,包含路径信息
- `types` 类型声明的是对 `node_modules/@types` 文件夹下的类型的依赖,不包含路径信息

/// 指令声明了对某个包的依赖。

例如,把/// 引入到声明文件,表明这个文件使用了@types/node/index.d.ts里面声明的名字; 并且,这个包需要在编译阶段与声明文件一起被包含进来。

仅当在你需要写一个d.ts文件时才使用这个指令。

参考链接:

zhongsp.gitbook.io/typescript-…

www.teaspect.com/detail/5586 二十二、TS+Webpack

webpack.config.js:

module.exports = { mode: 'development', entry: './src/main.ts', output: { path: __dirname + '/dist', filename: 'bundle.js' }, devtool: "source-map", resolve: { extensions: [ ".ts" ] }, module: { rules: [ { test: /.ts$/, exclude: /node_modules/, use: [ { loader: "ts-loader" } ] } ] } }

package.json:

{ "name": "m-ts-webpack", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "ts-loader": "^7.0.2", "typescript": "^3.8.3", "webpack": "^4.43.0", "webpack-cli": "^3.3.11" } }

项目代码:

github.com/baweireact/… 二十三、yarn清缓存和卸载全局安装的包

yarn cache clean

yarn global remove @vue/cli

yarn global add @vue/cli

二十四、修改脚手架使用npm下载还是使用yarn下载

二十五、倒计时

interface IObj { endtime: string, wrapper: HTMLElement }

class CountDown { endtime: string wrapper: HTMLElement constructor(obj: IObj) { this.endtime = obj.endtime this.wrapper = obj.wrapper } start() { setInterval(() => { let start = new Date().getTime() let end = new Date(this.endtime).getTime(); let result = end - start let time = this.formatDateTime(result) console.log(time) this.wrapper.innerHTML = time }, 1000) }

formatDateTime(shijiancha: number) { let days = shijiancha / 1000 / 60 / 60 / 24; let daysRound = Math.floor(days); let hours = shijiancha / 1000 / 60 / 60 - (24 * daysRound); let hoursRound = Math.floor(hours); let minutes = shijiancha / 1000 / 60 - (24 * 60 * daysRound) - (60 * hoursRound); let minutesRound = Math.floor(minutes); let seconds = shijiancha / 1000 - (24 * 60 * 60 * daysRound) - (60 * 60 * hoursRound) - (60 * minutesRound);

return `限时特卖 ${daysRound}${hoursRound}${minutesRound}${Math.floor(seconds)}秒`

} }

let countDown = new CountDown({ endtime: '2020/10/1', wrapper: document.getElementById('box') as HTMLElement }) countDown.start()

二十六、找出英文句子中最长的单词

function longest(str?:string) { if (typeof str === 'undefined') { alert('未传递参数') return } let arr = str.split(' ') let result = 0 let longestWord = '' for (let i = 0; i < arr.length; i++) { if (arr[i].length > result) { result = arr[i].length longestWord = arr[i] } } console.log(最长的单词是:${longestWord}) return result }

let result = longest('let life be beautiful like summer flowers') console.log(result)

longest()

参考链接:

一起来拥抱强大的TypeScript吧--Ts+Vue完全教程

中文手册

英文手册

入门教程

装饰器----阮一峰

Learn React with TypeScript 3(英文版):

链接:pan.baidu.com/s/1BriHov6-… 提取码:7erx