TS快速入门

目录

一、快速上手

二、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下载

二十五、倒计时

参考链接:


一、快速上手

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

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

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

TypeScript是微软开发的语言。

vue3.0使用ts开发。

是github开源项目:GitHub - microsoft/TypeScript: TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

2012 年 10 月诞生。

vscode是用ts编写的:GitHub - microsoft/vscode: Visual Studio Code

装包:

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:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="./index.js"></script>
</body>
</html>

效果:

自动补全功能:

二、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<RouteConfig> = [
  {
    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<RouteConfig> = [
  {
    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

Overview | Vue Class Component

https://github.com/vuejs/vue-class-component#readme

annotating:注释

加加减减:

<template>
  <div>
    <div>{{count}}</div>
    <button @click="handleSub">减</button>
    <button @click="handleAdd">加</button>
  </div>
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
class Login extends Vue {
  count = 0

  handleSub() {
    this.count--
  }
  
  handleAdd() {
    this.count++
  }
}

export default Login
</script>

或者:

<template>
  <div>
    <div>{{count}}</div>
    <button @click="handleSub">减</button>
    <button @click="handleAdd">加</button>
  </div>
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
class Login extends Vue {
  data() {
    return {
      count: 0
    }
  }

  handleSub() {
    this.count--
  }
  
  handleAdd() {
    this.count++
  }
}

export default Login
</script>

v-model:

<template>
  <div>
    <input v-model="username">
  </div>
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
class Login extends Vue {
  username = 'admin'
}

export default Login
</script>

挂载完生命周期:

<template>
  <div>
    1
  </div>
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
class Login extends Vue {
  mounted () {
    console.log('挂载完')
  }
}

export default Login
</script>

计算属性:

<template>
  <div>
    {{double}}
  </div>
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
class Login extends Vue {
  count = 1
  get double() {
    return this.count * 2
  }
}

export default Login
</script>

父子组件传值:

父组件

<template>
  <div>
    <Icon :name="visible ? 'xianshimima' : 'buxianshimima'" @onClick="handleVisible"></Icon>
  </div>
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'
import Icon from '../components/Icon'

@Component({
  components: {
    Icon
  }
})
class Login extends Vue {
  visible = false
  
  handleVisible() {
    this.visible = !this.visible
  }
}

export default Login
</script>

子组件

<template>
  <span :class="[`icon iconfont icon-${name}`]" @click="handleClick"></span>
</template>

<script lang="ts">
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'

@Component
class Icon extends Vue {
  @Prop()
  name: string

  handleClick() {
    this.$emit('onClick')
  }
}

export default Icon
</script>

子组件也可以写成这种

<template>
  <span :class="[`icon iconfont icon-${name}`]" @click="handleClick"></span>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'

const IconProps = Vue.extend({
  props: {
   name: String 
  }
})

@Component
class Icon extends IconProps {
  handleClick() {
    this.$emit('onClick')
  }
}

export default Icon
</script>

增加路由hook:

vue-class-component.ts

import Component from 'vue-class-component'

Component.registerHooks([
  "beforeRouteEnter"
])

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

在组件里可以使用

<script>
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class Test2 extends Vue {
  beforeRouteEnter(to, from, next) {
    console.log(1)
    next()
  }
}
</script>

四、vue-property-decorator

vue-property-decorator - npm

vue属性装饰器

父子组件传值,传参

父组件:

<template>
  <div>
    <Icon :name="visible ? 'show' : 'hide'" @onClick="handleVisible"></Icon>
  </div>
</template>

<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
import Icon from '../components/Icon.vue'

@Component({
  components: {
    Icon
  }
})
class Login extends Vue {
  visible = false
  
  handleVisible(payload:object) {
    this.visible = !this.visible
  }
}

export default Login
</script>

子组件:

<template>
  <span :class="[`icon iconfont icon-${name} ${className}`]" @click="handleClick"></span>
</template>

<script lang="ts">
import { Vue, Component, Prop, Emit } from 'vue-property-decorator'

@Component
class Icon extends Vue {
  @Prop({ default: 'zhanwei' })
  name!: string

  @Prop({ default: '' })
  className!: string

  @Emit('onClick')
  handleClick() {
    return { id: 2 }
  }
}

export default Icon
</script>

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

TypeScript 不适合在 vue 业务开发中使用吗? - 知乎

六、Property 'name' has no initializer

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

解决办法一:

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

解决办法二:

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

<template>
  <span :class="[`icon iconfont icon-${name}`]" @click="handleClick"></span>
</template>

<script lang="ts">
import { Vue, Component, Prop, Emit } from 'vue-property-decorator'

@Component
class Icon extends Vue {
  @Prop({ default: 'zhanwei' })
  name!: string

  @Emit('onClick')
  handleClick() {
    return { id: 2 }
  }
}

export default Icon
</script>

七、下载量对比

八、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<Vue>处理

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

vuex-class - npm

使用仓库做加加减减

仓库:

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: {
  }
})

页面:

<template>
  <div>
    <div>{{count}}</div>
    <button @click="handleSub">减</button>
    <button @click="handleAdd">加</button>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
import { State, Mutation } from 'vuex-class'

@Component
class Login extends Vue {
  @State('count') count!:number
  @Mutation('setState') setState!:Function

  handleSub() {
    let count = this.count - 1
    this.setState({ key: 'count', value: count })
  }

  handleAdd() {
    let count = this.count + 1
    this.setState({ key: 'count', value: count })
  }
}

export default Login
</script>

十二、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<number> = [1]
let strArr1:Array<string> = ['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<string> = ['a', 'b', 'c']

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

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

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

//泛型类
class Min<T> {
  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<T> {
  (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 - tc39/proposal-decorators: Decorators for ES6 classes

Decorators proposal

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

@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<T> {
  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)
  }
})

使用:

<template>
  <div>
    <div>{{msg}}</div>
    <div>{{count}}</div>
    <button @click="handleSub(1)">减</button>
    <button @click="handleAdd(1)">加</button>
    <TestComponent name="hello"></TestComponent>
  </div>
</template>

<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
import { Log, LogPlus } from '../utils/decorators'
import TestComponent from '../components/TestComponent.vue'

@Component({
  components: {
    TestComponent
  }
})
export default class Test extends Vue {
  msg = 'hello'
  count = 0

  @Log
  handleAdd(step:number) {
    this.count += step
  }

  @LogPlus('减法')
  handleSub(step:number) {
    this.count -= step
  }
}
</script>

十七、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 策略,会存在跨域问题https://stackoverflow.com/questions/20424279/canvas-todataurl-securityerror
      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<HTMLInputElement>, field: string) {
    // @ts-ignore
    this.setState({
      [field]: e.target.value
    })
  }

HTMLInputElement:

keyUp事件:

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

点击事件:

  handleVisible(e: MouseEvent<Element>, 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}</div>
  ))

  return (
    <div className="m-sidebar">
      {sidebarDom}
    </div>
  )
}

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

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

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<HTMLInputElement>, field: string) {
    // @ts-ignore
    this.setState({
      [field]: e.target.value
    })
  }

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

  handleEnter(e: KeyboardEvent<HTMLInputElement>) {
    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 (
      <div>
        <div>
          <input value={username} onChange={(e) => this.handleInput(e, 'username')} placeholder="请输入用户名" autoFocus />
        </div>
        <div>
          <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"></Icon>
        </div>
        <button onClick={() => this.handleLogin()}>登录</button>
      </div>
    )
  }
}

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)}></span>
  )
}

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<IProps> {
  render() {
    const { name, className = '', onClick = () => {} } = this.props
    return (
      <span className={`icon iconfont icon-${name} ${className}`} onClick={(e) => onClick(e, 1)}></span>
    )
  }
}

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 (
    <div className="m-wrap">
      <Header></Header>
      <Switch>
        <Route path="/index/home" render={() => renderComponent(<Home></Home>, '小米书城')}></Route>
        <Route path="/index/my_books" render={() => renderComponent(<MyBooks></MyBooks>, '我的书包')}></Route>
        <Route path="/index/me" render={() => renderComponent(<Me></Me>, '个人中心')}></Route>
      </Switch>
      <Footer></Footer>
    </div>
  )
}

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

const mapDispatchToProps = (dispatch: Dispatch<any>) => {
  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`:

// 注册全局变量 $
<script src="path/to/jquery.js"></script>

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

// 错误,缺少名字 $ 的声明信息
// 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`,这个成员函数的参数也是字符串类型
// 声明 $ 的类型信息
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 文件
/// <reference path="./jquery.d.ts" />
$('body').html('hello world');

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

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

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

/// <reference path="./jquery.d.ts" />
/// <reference types="node" />
  • - `path` 类型声明的是对本地文件的依赖,包含路径信息
  • - `types` 类型声明的是对 `node_modules/@types` 文件夹下的类型的依赖,不包含路径信息

/// <reference types="..." />指令声明了对某个包的依赖。

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

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

参考链接:

https://zhongsp.gitbook.io/typescript-handbook/handbook/triple-slash-directives#less-than-reference-path-greater-than

取经猿、IT技术分享平台

二十二、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"
  }
}

项目代码:

https://github.com/baweireact/m-apps/tree/master/demo/ts/01TS%2BWebpack

二十三、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(英文版):

链接:百度网盘 请输入提取码
提取码:7erx

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐同保

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值